Java如何實(shí)現(xiàn)多個(gè)線程之間共享數(shù)據(jù)
實(shí)現(xiàn)多個(gè)線程之間共享數(shù)據(jù)
一、 如果每個(gè)線程執(zhí)行的代碼相同
可以使用同一個(gè)Runnable對(duì)象,這個(gè)Runnable對(duì)象中有那個(gè)共享數(shù)據(jù),例如:賣票系統(tǒng)
class Ticket implements Runnable{
private int tick = 20;
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tick>0){
//只能try,因?yàn)閞un是復(fù)寫了Runnable接口的run,接口的run沒(méi)有拋
try{Thread.sleep(100);}catch(Exception e){} //使用sleep不然執(zhí)行每個(gè)線程都會(huì)占用完畢
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args) {
//只建立了一個(gè)Ticket對(duì)象,內(nèi)存中只有一個(gè)tick成員變量,所以是共享數(shù)據(jù)
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();
}
}
輸出結(jié)果
Thread-0....sale : 20
Thread-1....sale : 19
.......
Thread-3....sale : 2
Thread-3....sale : 1
二、 如果每個(gè)線程執(zhí)行的代碼不同
1、具體實(shí)現(xiàn)
將共享數(shù)據(jù)封裝在另外一個(gè)對(duì)象中,然后將這個(gè)對(duì)象逐一傳遞給各個(gè)Runnable對(duì)象。每個(gè)線程對(duì)共享數(shù)據(jù)的操作方法也分配到那個(gè)對(duì)象身上去完成,這樣容易實(shí)現(xiàn)針對(duì)該數(shù)據(jù)進(jìn)行的各個(gè)操作的互斥和通信。
思想: 一個(gè)類提供數(shù)據(jù)和操作數(shù)據(jù)的同步方法,另外定義兩個(gè)線程通過(guò)構(gòu)造函數(shù)接收并操作數(shù)據(jù),在主函數(shù)中直接創(chuàng)建線程對(duì)象,即可完成操作(可以實(shí)現(xiàn)兩個(gè)內(nèi)部類,不用構(gòu)造方法傳值,使用final定義data局部變量)
例如: 設(shè)計(jì)4個(gè)線程,其中兩個(gè)線程每次對(duì)j增加1,另外兩個(gè)線程每次對(duì)j減少1
public class MultyThreadShareMethod1 {
public static void main(String[] args){
//將數(shù)據(jù)封裝到一個(gè)對(duì)象上,
ShareData2 data1 = new ShareData2();
//在runnable的構(gòu)造函數(shù)中直接傳入去操作
for(int i=0;i<2;i++){
new Thread(new MyRunnable1(data1)).start();
new Thread(new MyRunnable2(data1)).start();
}
}
}
//封裝共享數(shù)據(jù)和操作共享數(shù)據(jù)方法的類
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);
}
}
//增加的線程,需要傳入一個(gè)共享數(shù)據(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();
}
}
}
//減少的線程,需要傳入一個(gè)共享數(shù)據(jù)
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();
}
}
}
輸出結(jié)果
Thread-0 inc : 11
...
Thread-1 dec : 10
2、 技巧總結(jié)
要同步互斥的幾段代碼最好是分別放在幾個(gè)獨(dú)立的方法中,這些方法再放在同一個(gè)類中,這樣比較容易實(shí)現(xiàn)它們之間的同步互斥或通信。
極端且簡(jiǎn)單的方式,即在任意一個(gè)類中定義一個(gè)static的變量,這將被所有線程共享。
多線程之間共享數(shù)據(jù)的方式探討
方式一:代碼一致
如果每個(gè)線程執(zhí)行的代碼相同,可以用一個(gè) Runnable 對(duì)象,這個(gè) Runnable 對(duì)象中存放那個(gè)共享數(shù)據(jù)(賣票系統(tǒng)可以這樣做)。
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 {
// 共享的數(shù)據(jù)
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);
}
}
}
}
}
方式二:代碼不一致
如果每個(gè)線程執(zhí)行的代碼不同時(shí),就需要不同的 Runnable 對(duì)象:
a. 將共享數(shù)據(jù)封裝在一個(gè)對(duì)象中,然后將這個(gè)對(duì)象逐一傳遞給各個(gè) Runnable 對(duì)象,每個(gè)線程對(duì)共享數(shù)據(jù)操作的方法也分配到這個(gè)對(duì)象中,這樣容易實(shí)現(xiàn)針對(duì)該數(shù)據(jù)進(jìn)行的各個(gè)操作的互斥通信。
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++){
//對(duì)數(shù)據(jù)進(jìn)行增加
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++){
//對(duì)數(shù)據(jù)進(jìn)行減少
this.data.decrement();
}
}
}
/**
將共享的數(shù)據(jù)封裝成一個(gè)類
*/
class ShareData{
//共享數(shù)據(jù)
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 對(duì)象作為某一個(gè)類的內(nèi)部類,共享數(shù)據(jù)作為這個(gè)外部類的成員變量,每個(gè)線程對(duì)共享數(shù)據(jù)的操作方法也分配到外部類中,實(shí)現(xiàn)共享數(shù)據(jù)的互斥和通信操作,作為內(nèi)部類的各個(gè) Runnable 對(duì)象調(diào)用外部類的這些方法。
public class MultiThreadShareData {
private int shareData=0;
public static void main(String[] args) {
MultiThreadShareData m=new MultiThreadShareData();
//初始化Runnable對(duì)象
MyRunnable1 myRunnable1 = m.new MyRunnable1();
MyRunnable2 myRunnable2=m.new MyRunnable2();
//開(kāi)啟線程
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);
}
/**
* 作為內(nèi)部類的Runnable對(duì)象
*/
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. 以上兩種方法的組合:將共享數(shù)據(jù)封裝到一個(gè)對(duì)象中,每個(gè)線程對(duì)共享數(shù)據(jù)的操作方法也分配到對(duì)象中,對(duì)象作為外部類的成員變量或方法中的局部變量,每個(gè)線程的 Runnable 作為成員內(nèi)部類或局部?jī)?nèi)部類。
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();
}
}
/**
封裝共享數(shù)據(jù)的對(duì)象
*/
class ShareData{
//共享數(shù)據(jù)
private int j=0;
/**
對(duì)共享數(shù)據(jù)進(jìn)行操作的方法
*/
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;
}
}
總之,要同步互斥的幾段代碼最好放在幾個(gè)獨(dú)立的方法中,這些方法再放入一個(gè)類中,這樣比較容易實(shí)現(xiàn)它們之間的同步互斥和通信。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring如何消除代碼中的if-else/switch-case
這篇文章主要給大家介紹了關(guān)于Spring如何消除代碼中if-else/switch-case的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Spring+SpringMVC配置事務(wù)管理無(wú)效原因及解決辦法詳解
這篇文章主要介紹了Spring+SpringMVC配置事務(wù)管理無(wú)效原因及解決辦法詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下2017-12-12
SSH框架網(wǎng)上商城項(xiàng)目第7戰(zhàn)之整合Struts2和Json
SSH框架網(wǎng)上商城項(xiàng)目第7戰(zhàn)之整合Struts2和Json,打通EasyUI和Struts2之間的交互,感興趣的小伙伴們可以參考一下2016-05-05
SPRING BOOT啟動(dòng)命令參數(shù)及源碼詳析
這篇文章主要給大家介紹了關(guān)于SPRING BOOT啟動(dòng)命令參數(shù)及源碼分析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SPRING BOOT具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
mybatis中使用not?in與?in的寫法說(shuō)明
這篇文章主要介紹了mybatis中使用not?in與?in的寫法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
springboot+mybatis-plus實(shí)現(xiàn)內(nèi)置的CRUD使用詳解
這篇文章主要介紹了springboot+mybatis-plus實(shí)現(xiàn)內(nèi)置的CRUD使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

