实现多个线程之间共享数据
一、 如果每个线程执行的代码相同
可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统
class Ticket implements Runnable{
private int tick = 20;
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tick>0){
//只能try,因为run是复写了Runnable接口的run,接口的run没有抛
try{Thread.sleep(100);}catch(Exception e){} //使用sleep不然执行每个线程都会占用完毕
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args) {
//只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
输出结果
Thread-0....sale : 20
Thread-1....sale : 19
.......
Thread-3....sale : 2
Thread-3....sale : 1
二、 如果每个线程执行的代码不同
1、具体实现
将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
思想: 一个类提供数据和操作数据的同步方法,另外定义两个线程通过构造函数接收并操作数据,在主函数中直接创建线程对象,即可完成操作(可以实现两个内部类,不用构造方法传值,使用final定义data局部变量)
例如: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1
public class MultyThreadShareMethod1 {
public static void main(String[] args){
//将数据封装到一个对象上,
ShareData2 data1 = new ShareData2();
//在runnable的构造函数中直接传入去操作
for(int i=0;i<2;i++){
new Thread(new MyRunnable1(data1)).start();
new Thread(new MyRunnable2(data1)).start();
}
}
}
//封装共享数据和操作共享数据方法的类
class ShareData2{
private int j = 10;
public synchronized void increment() {
j++;
System.out.println(Thread.currentThread().getName()+" inc : "+j);
}
public synchronized void decrement() {
j--;
System.out.println(Thread.currentThread().getName()+" dec : "+j);
}
}
//增加的线程,需要传入一个共享数据
class MyRunnable1 implements Runnable {
private ShareData2 data;
public MyRunnable1(ShareData2 data) {
this.data = data;
}
@Override
public void run() {
for(int i=0;i<10;i++){
data.increment();
}
}
}
//减少的线程,需要传入一个共享数据
class MyRunnable2 implements Runnable {
private ShareData2 data;
public MyRunnable2(ShareData2 data) {
this.data = data;
}
@Override
public void run() {
for(int i=0;i<10;i++){
data.decrement();
}
}
}
输出结果
Thread-0 inc : 11
...
Thread-1 dec : 10
2、 技巧总结
要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥或通信。
极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。
多线程之间共享数据的方式探讨
方式一:代码一致
如果每个线程执行的代码相同,可以用一个 Runnable 对象,这个 Runnable 对象中存放那个共享数据(卖票系统可以这样做)。
public class MultiThreadShareData {
public static void main(String[] args) {
MyShareData shareData=new MyShareData();
//放入不同的线程中
new Thread(shareData).start();
new Thread(shareData).start();
}
}
class MyShareData implements Runnable {
// 共享的数据
private int count = 100;
@Override
public void run() {
while (count > 0) {
synchronized (this) {
if (count > 0) {
count--;
System.out.println(Thread.currentThread().getName() + " 减了1,count还剩:" + count);
}
}
}
}
}
方式二:代码不一致
如果每个线程执行的代码不同时,就需要不同的 Runnable 对象:
a. 将共享数据封装在一个对象中,然后将这个对象逐一传递给各个 Runnable 对象,每个线程对共享数据操作的方法也分配到这个对象中,这样容易实现针对该数据进行的各个操作的互斥通信。
public class MultiThreadShareData {
private int shareData=0;
public static void main(String[] args) {
ShareData data = new ShareData();
new Thread(new MyRunnable1(data)).start();
new Thread(new MyRunnable2(data)).start();
}
}
class MyRunnable1 implements Runnable{
private ShareData data;
public MyRunnable1(ShareData data){
this.data=data;
}
@Override
public void run() {
for(int i=0;i<100;i++){
//对数据进行增加
this.data.increment();
}
}
}
class MyRunnable2 implements Runnable{
private ShareData data;
public MyRunnable2(ShareData data){
this.data=data;
}
@Override
public void run() {
for(int i=0;i<100;i++){
//对数据进行减少
this.data.decrement();
}
}
}
class ShareData{
//共享数据
private int j=0;
public synchronized void increment(){
this.j++;
System.out.println(Thread.currentThread().getName()+":j增加了1后j="+j);
}
public synchronized void decrement() {
this.j--;
System.out.println(Thread.currentThread().getName()+":j减少了1后j="+j);
}
public int getJ() {
return j;
}
}
b. 将 Runnable 对象作为某一个类的内部类,共享数据作为这个外部类的成员变量,每个线程对共享数据的操作方法也分配到外部类中,实现共享数据的互斥和通信操作,作为内部类的各个 Runnable 对象调用外部类的这些方法。
public class MultiThreadShareData {
private int shareData=0;
public static void main(String[] args) {
MultiThreadShareData m=new MultiThreadShareData();
//初始化Runnable对象
MyRunnable1 myRunnable1 = m.new MyRunnable1();
MyRunnable2 myRunnable2=m.new MyRunnable2();
//开启线程
new Thread(myRunnable1).start();
new Thread(myRunnable2).start();
}
private synchronized void increment(){
this.shareData++;
System.out.println(Thread.currentThread().getName()+":shareData增加了1后shareData="+shareData);
}
private synchronized void decrement() {
this.shareData--;
System.out.println(Thread.currentThread().getName()+":shareData减少了1后shareData="+shareData);
}
class MyRunnable1 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
increment();
}
}
}
class MyRunnable2 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
decrement();
}
}
}
}
c. 以上两种方法的组合:将共享数据封装到一个对象中,每个线程对共享数据的操作方法也分配到对象中,对象作为外部类的成员变量或方法中的局部变量,每个线程的 Runnable 作为成员内部类或局部内部类。
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData data = new ShareData();
new Thread(()->{
for(int i=0;i<100;i++){
data.increment();
}
}).start();
new Thread(()->{
for (int j=0;j<100;j++) {
data.decrement();
}
}).start();
}
}
class ShareData{
//共享数据
private int j=0;
public synchronized void increment(){
this.j++;
System.out.println(Thread.currentThread().getName()+":j增加了1后j="+j);
}
public synchronized void decrement() {
this.j--;
System.out.println(Thread.currentThread().getName()+":j减少了1后j="+j);
}
public int getJ() {
return j;
}
}
总之,要同步互斥的几段代码最好放在几个独立的方法中,这些方法再放入一个类中,这样比较容易实现它们之间的同步互斥和通信。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。