1.线程的定义

①继承Thread类,将执行的任务逻辑放到run方法中,调用start方法来开启线程

 1 public class ThreadDemo {
 2     public static void main(String[] args) {
 3         TDemo t = new TDemo();
 4         // 开启线程
 5         t.start();
 6         for (int i = 0; i < 20; i++) {
 7             System.out.println("main:" + i);
 8         }
 9     }
10 }
11 
12 class TDemo extends Thread {
13     @Override
14     public void run() {
15         for (int i = 0; i < 9; i++) {
16             System.out.println("Thread:" + i);
17         }
18     }
19 }

②实现Runnable,重写run方法,需要利用Runnable对象来构建一个Thread对象从而启动线程

    由于java是单继承的,因此当一个类已经继承了父类时,便不能继承Thread类。而又希望启用线程,此时实现Runnable接口即可达到目的。

 1 public class RunnableDemo {
 2     public static void main(String[] args) {
 3         RDemo r = new RDemo();
 4         // 通过Runnable对象来构建一个Thread对象
 5         Thread t = new Thread(r);
 6         t.start();
 7         for (int i = 0; i < 20; i++) {
 8             System.out.println("main:" + i);
 9         }
10     }
11 }
12 
13 class RDemo implements Runnable {
14     @Override
15     public void run() {
16         for (int i = 0; i < 10; i++) {
17             System.out.println("Thread:" + i);
18         }
19     }
20 }

③实现Callable<T>,重写call方法

 1 import java.util.concurrent.Callable;
 2 import java.util.concurrent.ExecutionException;
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 import java.util.concurrent.Future;
 6 
 7 public class CallableDemo {
 8     public static void main(String[] args) throws InterruptedException, ExecutionException {
 9         ExecutorService es = Executors.newCachedThreadPool();
10         Future<String> f = es.submit(new CDemo());
11         System.out.println(f.get());
12     }
13 }
14 
15 class CDemo implements Callable<String> {
16     @Override
17     public String call() throws Exception {
18         return "hahah~~~";
19     }
20 }

 

2.线程的状态

【java学习笔记】线程

    创建:新创建了一个线程对象。

    就绪:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的执行权。

    运行:就绪状态的线程获取了CPU执行权,执行程序代码。

    阻塞: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

    死亡:线程执行完它的任务时。

 

3.常见的线程方法

    ① Thread(String name)        初始化线程的名字

    ② getName()                        返回线程的名字

    ③ setName(String name)     设置线程对象名

    ④ sleep()                              线程睡眠指定的毫秒数。

    ⑤ getPriority()                      返回当前线程对象的优先级   默认线程的优先级是5

    ⑥ setPriority(int newPriority)     设置线程的优先级。(最大的优先级是10,最小的1,默认是5)虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现。

    ⑦ currentThread()               返回CPU正在执行的线程的对象。

 

4.多线程的并发安全问题

    线程的执行不存在先后,相互抢占执行,抢占并不是只发生在线程执行的开始,而是发生在线程执行的每一步过程中。由于多个线程并发导致出现了一些不符合常理的数据的现象,即线程安全问题。

4.1出现线程安全的根本原因

        ①存在两个或者两个以上的线程对象共享同一个资源
        ②多线程操作共享资源的代码有多句

4.2线程安全问题的解决方案
1.可以使用同步代码块去解决。

1 synchronized(锁对象){
2     需要被同步的代码
3 }

     注意事项:
            ①锁对象可以是任意一个对象
            ②一个线程在同步块中sleep,并不会释放锁对象
            ③如果不存在线程安全问题,千万不要使用同步代码块,因为会降低效率
            ④锁对象必须是多线程共享的一个资源,否则锁不住


2.同步函数 就是使用synchronized修饰的方法


     注意事项:
            ①如果是一个非静态的同步函数,锁对象是this;如果是静态的同步函数,锁对象是当前函数所属的类的字节码文件(class)
            ②同步函数的锁对象是固定的,不能由开发者来指定

推荐使用同步代码块
    ①同步代码块的锁对象可以由开发者指定,方便控制。而同步方法是固定的。
    ②同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步

 

例:模拟取款,

 1 public class Bank {
 2     public static void main(String[] args) {
 3         //创建一个账户
 4         Account account = new Account("10086", 1000);
 5         //模拟两个线程对同一个账户取钱
 6         new DrawThread("张三",account,800).start();
 7         new DrawThread("李四", account, 900).start();
 8     }
 9 }
10 
11 class DrawThread extends Thread{
12     //模拟用户账户
13     private Account account;
14     //当前取钱线程所希望取得钱数
15     private double drawMoney;
16 
17     public DrawThread(String name, Account account, double drawMoney) {
18         super(name);
19         this.account = account;
20         this.drawMoney = drawMoney;
21     }
22 
23     @Override
24     public void run() {
25         /*
26          * 虽然java程序允许任何对象作为同步监视器,但是同步监视器的目的:
27          * 阻止两个线程对同一个共享资源进行并发访问,因此推荐使用可能被并发访问的共享资源
28          * 当做同步监视器
29          * 
30          * 加锁-修改-释放锁
31          * 
32          * 字节码文件
33          * 静态变量
34          * "锁对象"
35          * 
36          */
37         synchronized (account) {
38                 //synchronized (this) {              //非共享,失败
39         //synchronized (new String("")) {  //非共享,失败
40         //synchronized ("") {              
41             //账户余额大于取钱数目
42             if(account.getBalance() >= drawMoney) {
43                 //吐出钞票
44                 System.out.println(getName() + "取钱成功!吐出钞票:" + drawMoney);
45 
46 //                try {
47 //                    Thread.sleep(5000);
48 //                } catch (Exception e) {
49 //                    e.printStackTrace();
50 //                }
51 
52                 //修改余额
53                 account.setBalance(account.getBalance() - drawMoney);
54                 System.out.println("\t余额为:" + account.getBalance());
55             } else {
56                 System.out.println(getName() + "取钱失败!余额不足!");
57             }    
58         }
59     }
60 }
61 
62 class Account{
63     //封装账户编号、账户余额两个成员变量
64     private String accountNo;
65     private double balance;
66     
67     public Account(String accountNo, double balance) {
68         super();
69         this.accountNo = accountNo;
70         this.balance = balance;
71     }
72 
73     public String getAccountNo() {
74         return accountNo;
75     }
76 
77     public void setAccountNo(String accountNo) {
78         this.accountNo = accountNo;
79     }
80 
81     public double getBalance() {
82         return balance;
83     }
84 
85     public void setBalance(double balance) {
86         this.balance = balance;
87     }
88 }
synchronized代码块Demo

相关文章: