Java之------多线程(从基础到加强及交互线程)

一、基础篇:

1、线程的定义

线程(thread)是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。

2、线程的属性

并发性,共享性,动态性,结构性

3、线程的状态

4、线程的调度

主要是通过实现Runnable接口和继承Thread类来实现线程的调度和操作

a、Runnable接口(里面就一个run方法,只要通过重写run方法就可以实现自己想要的线程功能)

[java] view
plain
 copy

  1. public interface Runnable
  2. {
  3. public abstract void run();
  4. }

b、Thread线程类(继承这个类)

[java] view
plain
 copy

  1. public class Thread extends Object implements Runnable
  2. {
  3. public Thread()                             //构造方法
  4. public Thread(String name)          //name指定线程名
  5. public Thread(Runnable target)   //target指定线程的目标对象
  6. public Thread(Runnable target, String name)
  7. public void run()                           //描述线程操作的线程体
  8. public final String getName()        //返回线程名
  9. public final void setName(String name)  //设置线程名
  10. public static int activeCount()       //返回当前活动线程个数
  11. public static Thread currentThread()   //返回当前执行线程对象
  12. public Sting toString()         //返回线程的字符串信息
  13. public void start()                          //启动已创建的线程对象
  14. }

★两种创建线程方式的比较

(1) 继承线程Thread类

public class NumberThread extends Thread

(2) 实现Runnable接口

public class NumberRunnable implements Runnable

1) 声明继承Thread类的奇数/偶数序列线程

a) main是首先启动执行的线程

b) 两个线程交替运行

[java] view
plain
 copy

  1. package thread.hello;
  2. public class MyThread extends Thread{
  3. private int num=0;
  4. public MyThread(int num) {
  5. this.num = num;
  6. }
  7. @Override
  8. public void run() {
  9. for (int i=num;i<=100;i+=2){
  10. System.out.print(i+" ");
  11. }
  12. System.out.println();
  13. }
  14. public static void main(String[] args) {
  15. Thread t1=new MyThread(1);
  16. Thread t2=new MyThread(2);
  17. t1.start();
  18. t2.start();
  19. }
  20. }

2) 声明实现Runnable接口的奇数/偶数序列线程

[java] view
plain
 copy

  1. package thread.hello;
  2. public class MyThread2 {
  3. public static void main(String[] args) {
  4. MyRun r1=new MyRun(1);
  5. MyRun r2=new MyRun(2);
  6. Thread t1=new Thread(r1);
  7. t1.start();
  8. Thread t2=new Thread(r2);
  9. t2.start();
  10. }
  11. }
  12. class MyRun implements Runnable {
  13. private int num=0;
  14. public MyRun(int num) {
  15. this.num = num;
  16. }
  17. @Override
  18. public void run() {
  19. for (int i=num;i<=100;i+=2){
  20. System.out.print(i+" ");
  21. }
  22. System.out.println();
  23. }
  24. }

★线程对象的优先级

1) Thread类中声明了3个表示优先级的公有静态常量:

[java] view
plain
 copy

  1. public static final int MIN__PRIORITY=1         //最低优先级
  2. public static final int MAX_PRIORITY=10         //最高优先级
  3. public static final int NORM_PRIORITY=5         //默认优先级

2) Thread类中与线程优先级有关的方法有以下2个:

[java] view
plain
 copy

  1. public final int getPriority()                //获得线程优先级
  2. public final void setPriority(int newPriority)//设置线程优先级

★线程对象的生命周期

Thread.State类声明的线程状态,新建态、运行态、阻塞态和等待态、终止态

★Thread类中改变和判断线程状态的方法

1) 线程启动

[java] view
plain
 copy

  1. public void start()                 //启动线程对象
  2. public final boolean isAlive()      //是否活动状态

2) 线程睡眠

[java] view
plain
 copy

  1. public static void sleep(long millis) throws InterruptedException

3) 线程中断

[java] view
plain
 copy

  1. public void interrupt()        //设置中断标记
  2. public boolean isInterrupted() //判断是否中断

例子(滚动字):

[java] view
plain
 copy

  1. package MyThread2;
  2. import java.awt.FlowLayout;
  3. import java.awt.GridLayout;
  4. import java.awt.event.ActionEvent;
  5. import java.awt.event.ActionListener;
  6. import javax.swing.JButton;
  7. import javax.swing.JFrame;
  8. import javax.swing.JLabel;
  9. import javax.swing.JOptionPane;
  10. import javax.swing.JPanel;
  11. import javax.swing.JTextField;
  12. public class WelcomeJFrame extends JFrame {
  13. public WelcomeJFrame(String[] texts){
  14. super("Rolling words");
  15. this.setBounds(500, 500, 400, 300);
  16. this.setDefaultCloseOperation(EXIT_ON_CLOSE);
  17. if (texts==null||texts.length==0) {
  18. this.getContentPane().add(new RollbyJPanel("Welcome"));
  19. }else {
  20. this.getContentPane().setLayout(new GridLayout(texts.length,1));
  21. for (int i = 0; i < texts.length; i++) {
  22. this.getContentPane().add(new RollbyJPanel(texts[i]));
  23. }
  24. }
  25. this.setVisible(true);
  26. }
  27. //  public WelcomeJFrame(){
  28. //      this(null);
  29. //  }
  30. public static void main(String args[]){
  31. String texts[]={"Hello","welcome","to","China"};
  32. new WelcomeJFrame(texts);
  33. }
  34. }
  35. class RollbyJPanel extends JPanel implements ActionListener,Runnable{
  36. private JTextField text_word,text_sleep,text_state;
  37. private JButton button_start,button_interrupt;
  38. private Thread thread_rollby;
  39. private int sleeptime;
  40. public RollbyJPanel(String str){
  41. this.setLayout(new GridLayout(2, 1));
  42. char space[]=new char[100];
  43. java.util.Arrays.fill(space,‘ ‘);
  44. text_word=new JTextField(str+new String(space));
  45. this.add(text_word);
  46. JPanel panel_sub=new JPanel();
  47. panel_sub.setLayout(new FlowLayout(FlowLayout.LEFT));
  48. this.add(panel_sub);
  49. panel_sub.add(new JLabel("sleep"));
  50. sleeptime=(int)(Math.random()*100);
  51. text_sleep=new JTextField(""+sleeptime);
  52. text_sleep.addActionListener(this);
  53. panel_sub.add(text_sleep);
  54. button_start=new JButton("start");
  55. button_start.addActionListener(this);
  56. panel_sub.add(button_start);
  57. button_interrupt=new JButton("interrupt");
  58. button_interrupt.addActionListener(this);
  59. panel_sub.add(button_interrupt);
  60. thread_rollby=new Thread(this);
  61. panel_sub.add(new JLabel("state"));
  62. text_state=new JTextField(""+thread_rollby.getState(),10);
  63. text_state.setEditable(false);
  64. panel_sub.add(text_state);
  65. }
  66. @Override
  67. public void actionPerformed(ActionEvent e) {
  68. if (e.getSource()==text_sleep) {
  69. try {
  70. sleeptime=Integer.parseInt(text_sleep.getText());
  71. } catch (NumberFormatException e1) {
  72. JOptionPane.showMessageDialog(this, "\""+text_sleep.getText()+"\""+"cannot be changed into an integer number");
  73. }
  74. }
  75. if (e.getSource()==button_start) {
  76. try {
  77. sleeptime=Integer.parseInt(text_sleep.getText());
  78. } catch (NumberFormatException e1) {
  79. JOptionPane.showMessageDialog(this, "\""+text_sleep.getText()+"\""+"cannot be changed into an integer number");
  80. }
  81. thread_rollby=new Thread(this);
  82. thread_rollby.start();
  83. text_state.setText(""+thread_rollby.getState());
  84. button_start.setEnabled(false);
  85. button_interrupt.setEnabled(true);
  86. }
  87. if (e.getSource()==button_interrupt) {
  88. thread_rollby.interrupt();
  89. text_state.setText(""+thread_rollby.getState());
  90. button_start.setEnabled(true);
  91. button_interrupt.setEnabled(false);
  92. }
  93. }
  94. @Override
  95. public void run() {
  96. while (true) {
  97. try {
  98. String str = text_word.getText();
  99. str = str.substring(1) + str.charAt(0);
  100. text_word.setText(str);
  101. Thread.sleep(sleeptime);
  102. } catch (InterruptedException e) {
  103. break;
  104. }
  105. }
  106. }
  107. }

结果界面:

二、交互线程:

1、运行结果不惟一,取决于线程调度

2、线程执行被打断时出现错误

3、线程互斥和临界区管理:操作系统对共享一个变量的若干线程进入各自临界区有以下3个调度原则:

1) 一次至多一个线程能够在它的临界区内。

2) 不能让一个线程无限地留在它的临界区内。

3) 不能强迫一个线程无限地等待进入它的临界区。特别地,进入临界区的任一线程不能妨碍正等待进入的其他线程的进展。

4、Java的线程互斥实现:

1) 同步语句

synchronized (对象)

语句

2) 同步方法

synchronized  方法声明

5、互斥的存/取款线程设计

Account:

[java] view
plain
 copy

  1. package thread3.bank3;
  2. public class Account {
  3. private String name; //储户姓名
  4. private double balance; //账户余额
  5. //开户
  6. public Account(String name){
  7. this.name = name;
  8. this.balance=0;
  9. }
  10. public String getName(){
  11. return name;
  12. }
  13. //查看余额
  14. public double balance(){
  15. return balance;
  16. }
  17. //存款
  18. public void put( double value){
  19. if(value>0){
  20. this.balance +=value;
  21. }
  22. }
  23. //取款: 如果余额不够就把剩余金额全部取出,如果余额足够则可取value
  24. public double get( double value){
  25. if(value>0){
  26. if(value <=balance){//存款够取
  27. this.balance -=value;
  28. }else{
  29. value = this.balance;
  30. this.balance = 0;
  31. }
  32. return value;
  33. }
  34. return 0;
  35. }
  36. synchronized public void fetchWork(double value){
  37. // 先看看有多少钱
  38. double howmuch = balance;
  39. // 模拟用户的操作时间差
  40. try {
  41. Thread.sleep(100);
  42. } catch (InterruptedException e) {
  43. e.printStackTrace();
  44. }
  45. // 取款
  46. double v = get(value);
  47. // 再看看还剩多少
  48. double remain = balance;
  49. // 信息输出
  50. System.out.println(name + "账户:现有" + howmuch + ",取出"
  51. + v + ",余额" + remain);
  52. }
  53. synchronized public void saveWork(double value){
  54. // 先看看账户中有多少钱
  55. double howmuch = balance;
  56. // 模拟用户的操作时间差
  57. try {
  58. Thread.sleep(100);
  59. } catch (InterruptedException e) {
  60. e.printStackTrace();
  61. }
  62. // 存钱
  63. put(value);
  64. // 再看看账户余额,以确认钱已经存进去了
  65. double b = balance;
  66. // 信息输出
  67. System.out.println(name+ "账户:现有" + howmuch + ",存入"
  68. + value + ",余额" + b);
  69. }
  70. }
  71. class Fetch extends Thread {
  72. private double value;// 取款金额
  73. private Account account;// 取款账户
  74. public Fetch(Account account, double value) {
  75. this.account = account;
  76. this.value = value;
  77. }
  78. @Override
  79. public void run() {
  80. this.account.fetchWork( this.value );
  81. }
  82. }
  83. class Save extends Thread {
  84. private double value;// 存款金额
  85. private Account account;// 存款账户
  86. public Save(Account account, double value) {
  87. this.account = account;
  88. this.value = value;
  89. }
  90. public void run() {
  91. account.saveWork(value);
  92. }
  93. }

Bank:

[java] view
plain
 copy

  1. package thread3.bank3;
  2. public class Bank {
  3. public static void main(String[] args) {
  4. //用同步方法:   synchrozized public void aa() { ...... }  //方法aa()的调用对象就是对象锁
  5. Account li = new Account("Li");
  6. Account wang = new Account("Wang");
  7. Save s1 = new Save(wang,100);
  8. Save s2 = new Save(wang,200);
  9. Save s5 = new Save(wang,300);
  10. Save s4 = new Save(wang,200);
  11. Fetch f1 = new Fetch(wang,300);
  12. Save s3 = new Save(li,100);
  13. s3.start();
  14. s2.start();
  15. s1.start();
  16. s4.start();
  17. s5.start();
  18. f1.start();
  19. }
  20. }

正确结果:

错误结果:

三、加强篇:

1、线程互斥锁

a、多线程互斥共享“基本数据类型数据”资源,锁(用synchronized关键字)的必须是对象,基本数据类型的变量不能当作对象锁,同时,要保证多线程使用的是同一个互斥锁(对象锁),才能进行同步。

b、多线程互斥共享“栈”资源

举例:多窗口买票

[java] view
plain
 copy

  1. package thread.ticket.v1;
  2. public class SellingTickets {
  3. public static void main(String[] args) {
  4. Window r1=new Window("窗口1");
  5. Thread t1=new Thread(r1);
  6. t1.start();
  7. Window r2=new Window("窗口2");
  8. Thread t2=new Thread(r2);
  9. t2.start();
  10. Window r3=new Window("窗口3");
  11. Thread t3=new Thread(r3);
  12. t3.start();
  13. Window r4=new Window("窗口4");
  14. Thread t4=new Thread(r4);
  15. t4.start();
  16. }
  17. }
  18. class Window implements Runnable{
  19. private static int num=200;
  20. //由于基本数据类型的资源无法用作对象锁,且它是类的静态成员,
  21. //因此可新建一个与共享的"基本数据类型"资源平行的对象,来代替它来做对象锁
  22. private static Object obj=new Object();
  23. private String windowName=null;
  24. public Window(String windowName) {
  25. this.windowName = windowName;
  26. }
  27. @Override
  28. public void run() {
  29. //      synchronized (obj) {
  30. //这里如果加了锁的话就会变成只有一个窗口把所有的票全部卖完了,不加的话就会是所有的窗口一起卖,
  31. //而且很少出现有重复的票,但是作为软件这样做很不安全,因为在其他机器上运行很有可能会出现有重复票的现象,
  32. //于是应该像下面这样把锁放到while里面去
  33. while (true){
  34. //这里不能用this来代替obj
  35. synchronized (obj) {//同步块---基本数据类型的变量不能当作互斥锁。因为互斥锁是对象锁
  36. if (num > 0) {
  37. System.out.println(windowName + ":" + num--);
  38. } else {
  39. break;
  40. }
  41. }
  42. }
  43. //      }
  44. }
  45. }

2、多线程调度

Java的多线程是抢占式的运行方式(先启动的线程抢占到资源的几率更大些)

1) setPriority()方法 :设置优先级

只要在一个线程启动之前为他调用这个方法就可以增加抢占到资源的概率,默认是5,越小抢占资源能力越强

2) sleep()方法和interrupt()方法 :Thread类的sleep()方法对当前线程操作,是静态方法,在执行sleep()方法时不释放对象锁。sleep()的参数指定以毫秒为单位的线程休眠时间。除非因为中断而提早恢复执行,否则线程不会在这段时间之前恢复执行。可以用interrupt()来提前中断sleep()方法,也可以用抛异常的方法中断。一个线程可以调用另外一个线程的interrupt()方法,这将向暂停的线程发出一个InterruptedException。变相起到唤醒暂停线程的功能。Thread类的方法interrupt(),是一种强制唤醒的技术。

[java] view
plain
 copy

  1. package thread.schedule.v1;
  2. public class Schedule {
  3. public static void main(String[] args) {
  4. Thread t1=new MyThread();
  5. Thread t2=new MyThread();
  6. t1.start();
  7. t2.start();
  8. try {
  9. Thread.sleep(2000);
  10. t1.interrupt();//过两秒钟的时候强制唤醒t1线程
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  16. class MyThread extends Thread{
  17. private static Object obj=new Object();
  18. @Override
  19. public void run() {
  20. synchronized (obj) {
  21. try {
  22. Thread.sleep(5000);
  23. } catch (InterruptedException e) {
  24. System.out.println(this.getName()+"已经被唤醒");
  25. }
  26. for (int i = 1; i <= 100; i++) {
  27. System.out.println(Thread.currentThread().getName() + "--NO--"
  28. + i);
  29. }
  30. }
  31. }
  32. }

3) yield() 方法:用来使具有相同优先级的线程获得执行的机会。如果具有相同优先级的其它线程是可运行的,yield()将把线程放到可运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则什么都不做。

注意:执行一次yield()方法,该线程只是放弃当前这一次机会,然后又会重新和其它线程一起抢占CPU,很可能又比其它线程先抢到。

[java] view
plain
 copy

  1. package cn.hncu.thread.schedule.v2;
  2. public class Schedule {
  3. public static void main(String[] args) {
  4. Thread t1=new MyThread("t1");
  5. Thread t2=new MyThread("t2");
  6. t1.start();
  7. //      try {
  8. //          t1.join();//这里如果用了join()就体现不出yield()方法了
  9. //      } catch (InterruptedException e) {
  10. //          e.printStackTrace();
  11. //      }
  12. System.out.println("main..........");
  13. t2.start();
  14. }
  15. }
  16. class MyThread extends Thread{
  17. private static Object obj=new Object();
  18. private String threadName=null;
  19. public MyThread(String threadName) {
  20. this.threadName = threadName;
  21. }
  22. @Override
  23. public void run() {
  24. //      synchronized (obj) {
  25. System.out.println(":::::::::" + threadName);
  26. int num = 0;
  27. while (this.threadName.equals("t1") && num++ < 50) {
  28. this.yield();//yield不会释放对象锁,因此,即使在外围环绕了synchronized也无法使该线程放弃,要一直到该线程执行完,在没有加锁的时候使用这个yield()方法的话每次t1线程到这里放弃了,但是他又会重新和t2线程抢资源
  29. }
  30. for (int i = 1; i <= 100; i++) {
  31. System.out.println(threadName + "--NO.--" + i);
  32. }
  33. //      }
  34. }
  35. }

4) join()方法:调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。它可以实现线程合并的功能,经常用于线程的绝对调度。

[java] view
plain
 copy

  1. package thread.schedule.v2;
  2. public class Schedule {
  3. public static void main(String[] args) {
  4. Thread t1=new MyThread("t1");
  5. Thread t2=new MyThread("t2");
  6. t1.start();
  7. try {
  8. t1.join();
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. System.out.println("main..........");
  13. t2.start();
  14. //注意,这里如果把t2放在t1之前启动的话那么t2照样会和t1抢资源,不会等t1运行完,所以没有调用join()方法的线程要后启动
  15. }
  16. }
  17. class MyThread extends Thread{
  18. private static Object obj=new Object();
  19. private String threadName=null;
  20. public MyThread(String threadName) {
  21. this.threadName = threadName;
  22. }
  23. @Override
  24. public void run() {
  25. System.out.println(":::::::::" + threadName);
  26. for (int i = 1; i <= 100; i++) {
  27. System.out.println(threadName + "--NO.--" + i);
  28. }
  29. }
  30. }

5) wait()方法:当前线程进入对象的wait pool。

6) notify()/notifyAll()方法:唤醒对象的wait pool中的一个/所有等待线程

注:wait和notify只能在它们被调用的实例的同步块内使用,而sleep()到处都可以用。

wait()和sleep()最大的区别:sleep()不释放对象锁,而wait()会释放,因此从效率方面考虑wait()方法更好。

3、死锁

死锁一:

[java] view
plain
 copy

  1. package thread.deadLock.lock1;
  2. public class DeadLock {
  3. public static void main(String[] args) {
  4. S s=new S();
  5. Thread b=new Thread(new ThreadB(s));
  6. Thread a=new Thread(new ThreadA(s,b));
  7. a.start();
  8. b.start();
  9. }
  10. }
  11. class S {
  12. public int a=0;
  13. }
  14. class ThreadA implements Runnable{
  15. private S s=null;
  16. private Thread b=null;
  17. public ThreadA(S s, Thread b) {
  18. this.s = s;
  19. this.b = b;
  20. }
  21. @Override
  22. public void run() {
  23. System.out.println("now start ThreadA------");
  24. synchronized (s) {//线程a先启动在这里拿到锁
  25. System.out.println(Thread.currentThread().getName()+"--A");
  26. try {
  27. b.join();//这里b线程调用join()方法,即原本是要等待b运行完其他线程才可以运行,可是这时候的锁还在a手中,因此出现了a线程在等待b线程,b线程在等待a线程的现象,这是一种死锁的现象
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. System.out.println("a="+s.a);
  32. }
  33. }
  34. }
  35. class ThreadB implements Runnable{
  36. private S s=null;
  37. public ThreadB(S s) {
  38. this.s = s;
  39. }
  40. @Override
  41. public void run() {
  42. System.out.println("new start ThreadB------");
  43. synchronized (s) {
  44. s.a=100;
  45. System.out.println(Thread.currentThread().getName()+"--B ,a="+s.a);
  46. }
  47. }
  48. }

死锁二:

[java] view
plain
 copy

  1. package thread.deadLock.lock2;
  2. public class DeadLock {
  3. public static void main(String[] args) {
  4. //如果要解决这种多资源出现的死锁,可以把多个资源打包成一个综合资源,
  5. //把综合资源变成一个对象锁,哪个线程一拿到锁就有全部资源了
  6. //在设计阶段就应该考虑到----把多线程中的每个线程所用的互斥资源图画出来--从图中看出哪些线程存在共享互斥资源,
  7. //然后分析是否可能存在死锁
  8. S1 s1=new S1();
  9. S2 s2=new S2();
  10. Thread a=new Thread(new ThreadA(s1,s2));
  11. Thread b=new Thread(new ThreadB(s1,s2));
  12. a.start();
  13. b.start();
  14. }
  15. }
  16. class S1 {
  17. public int a=1;
  18. }
  19. class S2 {
  20. public int a=2;
  21. }
  22. class ThreadA implements Runnable{
  23. private S1 s1=null;
  24. private S2 s2=null;
  25. public ThreadA(S1 s1, S2 s2) {
  26. this.s1 = s1;
  27. this.s2 = s2;
  28. }
  29. @Override
  30. public void run() {
  31. System.out.println("now start ThreadA------");
  32. synchronized (s1) {//这里a线程拿到锁
  33. System.out.println(Thread.currentThread().getName()+"--A");
  34. System.out.println("线程A输出,s1.a="+s1.a);
  35. System.out.println("线程A拿到锁s1,但在等待锁s2");
  36. synchronized (s2) {//在这里假如前面拿到锁s1的时候下面的b线程也拿到了锁s2那么这里就会出现死锁现象,下面的b线程也会出现死锁,因为a和b线程各握着彼此需要的一部分不放,因此无法继续进行下去,但也有可能在b线程没有拿到锁s2时a线程就一口气拿到锁s1和锁s2运行完了不出现死锁
  37. System.out.println("线程A输出,s2.a="+s2.a);
  38. }
  39. }
  40. }
  41. }
  42. class ThreadB implements Runnable{
  43. private S1 s1=null;
  44. private S2 s2=null;
  45. public ThreadB(S1 s1, S2 s2) {
  46. this.s1 = s1;
  47. this.s2 = s2;
  48. }
  49. @Override
  50. public void run() {
  51. System.out.println("now start ThreadB------");
  52. synchronized (s2) {
  53. System.out.println(Thread.currentThread().getName()+"--B");
  54. System.out.println("线程B输出,s2.a="+s2.a);
  55. System.out.println("线程B拿到锁s2,但在等待锁s1");
  56. synchronized (s1) {
  57. System.out.println("线程B输出,s1.a="+s1.a);
  58. }
  59. }
  60. }
  61. }

4、相关概念

1、创建线程和启动线程并不相同:在一个线程对新线程的Thread对象调用start()方法之前,这个线程并没有真正开始执行。Thread对象在其线程真正启动之前就已经存在了,而且其线程退出之后仍然存在。因此,仍可以控制或获取关于已创建的线程的信息,即使线程还没有启动或已经完成了。

2、结束线程:

1)线程到达其run()方法的末尾,推荐这种方法,自然结束。

2)线程抛出一个未捕获到的Exception或Error。

3)另一个线程调用一个弃用的stop()方法(不建议使用)。

3、守护程序线程(简称守护线程):我们提到过当Java程序的所有线程都完成时,该程序就退出,但这并不完全正确,因为程序中还隐藏的系统线程。随着程序的启动而启动,在运行期间一直捕捉符合它条件的处理,这样的线程就是守护线程。

5、注意问题

1、synchronized必须锁的是对象,基本数据类型的变量不能当作对象锁。

2、要保证多线程使用的是同一个互斥锁(对象锁),才能进行同步。

3、死锁的两种情况:

1)多个线程共用同一个对象锁,互相等待。

2)互相持有对方所需的资源(即每个线程都需要同时拿到多个资源才能继续执行,而多个线程都处于:各持有一部分,在等待另一部分。)

4、死锁的解决:要从设计方面去解决避免,即在设计时就考虑不能出现死锁。

罗列出所有临界资源,画分布图,从图中观察其中的死锁情况,改变其中线程的(临界)资源的获取方式。

设计原则:尽量让程序中少出现临界资源。

5、wait/notify 和 sleep方法:wait和notify只能在它们被调用的实例的同步块内使用,而sleep()到处都可以用。wait()和sleep()最大的区别:sleep()不释放对象锁,而wait()会释放,因此从效率方面考虑wait()方法更好。

6、同步设计的基本原则:同步块中(synchronized修饰)的代码越小越好!

同步块中不要写阻塞性代码(如,InputStream.read() )!

在持有锁的时候,不要对其它对象调用方法。(如果做到,可以消除最常见的死锁源头。)

7、同步概述:

同步的原理:将需要同步的代码进行封装,并在该代码上加了一个锁。

同步的好处:解决多线程的安全问题。

同步的弊端:会降低性能。

同步的前提:必须要保证有多个线程且它们在同步中使用的是同一个锁。

时间: 2024-11-05 21:49:16

Java之------多线程(从基础到加强及交互线程)的相关文章

Java之多线程同步基础

java学习的道路上呢总有一些麻烦的东西需要花费一些时间去理解,比如个人认为不好搞的多线程. 线程是并列运行的 因为是并列运行,所以有时候会发生资源抢占,从而导致参数变化; 比如酱紫 package seer.线程; public class SumArray { private int sum; //在这个地方sumArry()没有被同步 没有加sync... public int sumArray(int[] sums) { sum = 0; //重置 初始化sum for (int i =

[Java][Android] 多线程同步-主线程等待全部子线程完毕案例

有时候我们会遇到这种问题:做一个大的事情能够被分解为做一系列相似的小的事情,而小的事情无非就是參数上有可能不同样而已! 此时,假设不使用线程,我们势必会浪费许多的时间来完毕整个大的事情.而使用线程的话将会存在这种问题: 主线程启动全部子线程并发运行后主线程就直接返回了,导致外部函数判读整个大的事情完毕了,可是实际上并没有完毕! 针对以上情况我想我会採用多线程方式运行同一时候解决主线程等待子线程的问题.如图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQ

Java实现多线程经典问题:使用三个线程实现输出ABCABC循环

使用三个线程实现ABCABC--循环. 代码如下: //标记类,用来让三个线程共享,同时也是三个线程中同步代码快的标记对象. //之前这个标记我设置成Integer,但是发现Integer进行加法运算时会改变对 //象引用(原因是自动装箱),因此出现异常抛出.所以索性自己定义Flag类. class Flag{ int i=0; public synchronized void setI() { i++; if(i==3) i=0; } } //输出A的线程 class SafeTestA im

java基础九[网络与线程](阅读Head First Java记录)

网络socket连接 Java API的网络功能包(java.net)已经将底层的TCP连接等都封装好了,我们只需要通过Socket对象来建立客户端和服务器的连接,然后客户端能向服务器发送请求,并接收服务端发来的数据即可 服务端和客户端大概的交互如下所示: 编写客户端程序 第一步:建立socket连接 需要客户端和服务器端都建立以下连接 Socket chatSocket=new Socket(“对方IP地址”,TCP端口号); IP类似门牌号,做寻址,找到服务器.端口号是找到这台服务器上的某个

沉淀再出发:再谈java的多线程机制

沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于java的多线程机制以及其中延伸出来的很多概念和相应的实现方式一直都是模棱两可的,虽然后来在面试的时候可能恶补了一些这方面的知识,但是也只是当时记住了,或者了解了一些,等到以后就会变得越来越淡忘了,比如线程的实现方式有两三种,线程池的概念,线程的基本生命周期等等,以及关于线程之间的多并发引起的资源的抢占

Java的多线程实现生产/消费模式

Java的多线程实现生产/消费模式 在Java的多线程中,我们经常使用某个Java对象的wait(),notify()以及notifyAll() 方法实现多线程的通讯,今天就使用Java的多线程实现生产/消费模式,需求如下: 线程A ProductThread 继承Thread 实现生产数据 若线程共享的数据不为NULL,则生产线程进入等待状态 线程B CustomThread 继承Thread 实现消费数据(输出到控制台) 当线程共享数据为NULL的时候,进入等待状态 线程B 消费完数据之后,

Java多线程编程基础之线程对象

在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念. [线程的并发与并行] 在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent).而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel). 在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储

java笔记--多线程基础

多线程技术 在java中实现多线程技术有两种方式: 1.继承Thread类: 2.实现Runnable接口 这两种方法都需要重写run()方法:通常将一个新线程要运行的代码放在run()方法中(这是创建没有返回值线程的方法)由于java只支持单继承,当类已经继承有其他类时,只能选择实现Runnable接口在启动线程时需要使用Thread类的start()方法,而不是直接使用run()方法: 如: public static void function() { for (int i = 0; i

JAVA思维导图系列:多线程0基础

感觉自己JAVA基础太差了,又一次看一遍,已思维导图的方式记录下来 多线程0基础 进程 独立性 拥有独立资源 独立的地址 无授权其它进程无法訪问 动态性 与程序的差别是:进程是动态的指令集合,而程序是静态的指令集合 增加时间概念 有自己的生命周期和不同的状态 并发性 多个进程能够在单核处理器并发运行 多个进程互不影响 和并行的差别:并行是同一时刻多个进程在多个处理器上同一时候运行 而并发是指在同一时刻仅仅能运行一条指令,但互相切换迅速,宏观上看是运行多个指令 线程 线程相对于进程如同进程相对于操