发布于2021-03-07 20:01 阅读(1123) 评论(0) 点赞(3) 收藏(5)
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
package mythread;
public class MyThread1 extends Thread
{
private String name;
public MyThread1(String name)
{
this.name = name;
}
public void run()
{
System.out.println("hello " + name);
}
public static void main(String[] args)
{
Thread thread = new MyThread1("world");
thread.start();
}
}
package mythread;
public class MyThread2 implements Runnable
{
private String name;
public void setName(String name)
{
this.name = name;
}
public void run()
{
System.out.println("hello " + name);
}
public static void main(String[] args)
{
MyThread2 myThread = new MyThread2();
myThread.setName("world");
Thread thread = new Thread(myThread);
thread.start();
}
}
package mythread;
class Data
{
public int value = 0;
}
class Work
{
public void process(Data data, Integer numbers)
{
for (int n : numbers)
{
data.value += n;
}
}
}
public class MyThread3 extends Thread
{
private Work work;
public MyThread3(Work work)
{
this.work = work;
}
public void run()
{
java.util.Random random = new java.util.Random();
Data data = new Data();
int n1 = random.nextInt(1000);
int n2 = random.nextInt(2000);
int n3 = random.nextInt(3000);
work.process(data, n1, n2, n3); // 使用回调函数
System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+"
+ String.valueOf(n3) + "=" + data.value);
}
public static void main(String[] args)
{
Thread thread = new MyThread3(new Work());
thread.start();
}
}
package com.example.paoduantui.Thread;
/**
* 线程通信的例子:使用两个线程打印1—100,线程1,线程2交替打印
*
* 当我们不采取线程之间的通信时,无法达到线程1,2交替打印(cpu的控制权,是自动分配的)
* 若想达到线程1,2交替打印,需要:
* 1.当线程1获取锁以后,进入代码块里将number++(数字打印并增加)操作完以后,为了保证下个锁为线程2所有,需要将线程1阻塞(线程1你等等wait())。(输出1,number为2)
* 2.当线程2获取锁以后,此时线程1已经不能进入同步代码块中了,所以,为了让线程1继续抢占下一把锁,需要让线程1的阻塞状态取消(通知线程1不用等了notify()及notifyAll()),即应该在进入同步代码块时取消线程1的阻塞。
*
* */
class Number implements Runnable{
private int number = 1;//设置共享数据(线程之间对于共享数据的共享即为通信)
//对共享数据进行操作的代码块,需要线程安全
@Override
public synchronized void run() {
while(true){
//使得线程交替等待以及通知交替解等待
notify();//省略了this.notify()关键字
if(number<100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
public class CommunicationTest {
public static void main(String[] args){
//创建runnable对象
Number number = new Number();
//创建线程,并实现runnable接口
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2");
//开启线程
t1.start();
t2.start();
}
}
wait()/ notify()/ notifayAll():此三个方法定义在Object类中的,因为这三个方法需要用到锁,而锁是任意对象都能充当的,所以这三个方法定义在Object类中。
wait(在进入锁住的区域以后阻塞等待,释放锁让别的线程先进来操作)---- Obj.wait 进入Obj这个锁住的区域的线程把锁交出来原地等待通知
notify(由于有很多锁住的区域,所以需要将区域用锁来标识,也涉及到锁) ----- Obj.notify 新线程进入Obj这个区域进行操作并唤醒wait的线程
所以wait,notify需要使用在有锁的地方,也就是需要用synchronize关键字来标识的区域,即使用在同步代码块或者同步方法中,且为了保证wait和notify的区域是同一个锁住的区域,需要用锁来标识,也就是锁要相同的对象来充当
1.先判断是否多线程
2.再判断是否有共享数据
3.是否并发的对共享数据进行操作
4.选择上述三种方法解决线程安全问题
package com.example.paoduantui.Thread;
/***
* 描述:甲乙同时往银行存钱,存够3000
*
*
* */
//账户
class Account{
private double balance;//余额
//构造器
public Account(double balance) {
this.balance = balance;
}
//存钱方法
public synchronized void deposit(double amt){
if(amt>0){
balance +=amt;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
}
}
}
//两个顾客线程
class Customer extends Thread{
private Account acct;
public Customer(Account acct){
this.acct = acct;
}
@Override
public void run() {
for (int i = 0;i<3;i++){
acct.deposit(1000);
}
}
}
//主方法,之中new同一个账户,甲乙两个存钱线程。
public class AccountTest {
public static void main(String[] args){
Account acct = new Account(0);
Customer c1 = new Customer(acct);
Customer c2 = new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
package com.example.paoduantui.Thread;
public class Bank {
//私有化构造器
private Bank(){}
//初始化静态实例化对象
private static Bank instance = null;
//获取单例实例,此种懒汉式单例模式存在线程不安全问题(从并发考虑)
public static Bank getInstance(){
if(instance==null){
instance = new Bank();
}
return instance;
}
//同步方法模式的线程安全
public static synchronized Bank getInstance1(){
if(instance==null){
instance = new Bank();
}
return instance;
}
//同步代码块模式的线程安全(上锁)
public static Bank getInstance2(){
synchronized (Bank.class){
if(instance==null){
instance = new Bank();
}
return instance;
}
}
//效率更高的线程安全的懒汉式单例模式
/**
* 由于当高并发调用单例模式的时候,类似于万人夺宝,只有第一个进入房间的人才能拿到宝物,
* 当多个人进入这个房间时,第一个人拿走了宝物,也就另外几个人需要在同步代码块外等候,
* 剩下的人只需要看到门口售罄的牌子即已知宝物已经被夺,可以不用进入同步代码块内,提高了效率。
*
*
* */
public static Bank getInstance3(){
if (instance==null){
synchronized (Bank.class){
if(instance==null){
instance = new Bank();
}
}
}
return instance;
}
}
https://blog.csdn.net/gf771115/article/details/51682561
https://blog.csdn.net/weixin_44797490/article/details/91006241
作者:javabb
链接:http://www.javaheidong.com/blog/article/110481/c8574e04a6f1d0ca76e3/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!