------- android培训、java培训、期待与您交流! ----------
package com.itheima;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 进程: 是一个正在执行的程序。
* 每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元
* 线程:就是进程中的一个独立单元,线程控制着进程的执行。
* 一个进程至少有一个线程
*
* Jvm 启动时会有一个进程java.exe
* 该进程中至少有一个县城负责java程序的执行
* 而且这个线程运行的代码在main方法中,该线程为主线程
*
* 创建线程第一种方式:继承Thread类
* 1.定义类继承Thread
* 2.复写Thread类中的run() 目的:将定义的代码存储在run方法,让线程运行
* 3.调用线程的start方法 该方法有两个作用:1.启动线程 2.调用run()
* @param args
*/
class myThread1 extends Thread{
private int i=1;
public void run(){
while(true){
System.out.println(i++);
}
}
}
/**
* 创建线程方式二:实现Runnable接口
* 1.定义类实现Runnable接口
* 2.覆盖Runnable接口中的run()
* 3.通过Thread类创建线程对象
* 4.将Runnable接口的子类做为参数传递给Thread类的构造函数
* 5.调用Thread类的start方法开启线程
* */
class MyThread2 implements Runnable{
int i=1;
public void run(){
while(true){
System.out.println(i++);
}
}
}
/**
* 实现方式和继承方式的区别
* 实现方式的好处:避免了单继承的局限性 ,在定义线程是,建议使用实现的方式
* 继承Thread:线程代码存放在Thread子类的run方法中
* 实现Runnable:线程代码存放在实现接口的子类run方法中
* */
//需求:做一个像买票一样的窗口
class myThread3 extends Thread{
private static int ticket=1000;
public void run(){
if(ticket>0)
System.out.println(ticket--);
}
}
/**
* 在创建多个myThread3的对象时,发现没一次运行结果多不同
* 因为多个线程同时获取cpu执行权,cpu执行到谁,谁就运行
* 明确一点,在某一个时刻,只能运行一个程序的运行(多核除外)
* cpu在坐着快速的切换,所以看上去是同事运行的效果
* 我们可以形象的把多线程的行为看作是互相抢夺cpu执行权
*
* 这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长cpu说的算
* */
/**
* 多线程的六种状态:
* 1.被创建 2.运行 3.冻结(没有放弃运行资格,休息一会) 4.冻结(放弃运行资格)
* 5.临时状态 即阻塞状态 6.消亡
* */
/*
* 线程都有自己默认的名称 Thread-编号 从0开始
* Thread(String name); 分配给Thread对象名称
*
* static void currentThread() 返回当前这在执行党的线程的线程
* 设置线程的名称:
* 1.Thread.setName(Stirng name);
* 2.通过构造函数
*
* */
/*线程安全问题的原因:
* 1.多个线程在操作共享的数据;
* 2.操作共享数据的线程代码有多条
* 当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会
* 导致线程安全问题的产生
*/
//线程安全的事例
class Ticket implements Runnable{
private static int num=1000;
boolean flag = true;
public void run(){
if(flag){
while(true){
if(num>0){
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"::"+num--);
}
}
}
}
}
// 会打印多个错误的号码,有的num也会重复,也会出现负数的现象
/*
* 解决办法:对多余操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其它线程不参与执行
*
* java对于多线程的安全问题,提供了专业的解决方案,就是同步代码块
* synchronized(对象){
* 需要执行的代码
* }
* 这里的对象如同锁,持有锁的线程可以在同步中执行,没有锁的线程,即使获取了cpu执行权,也进不去因为没有锁的获取
*
* 同步的好处:解决了线程的安全问题;
* 同步的弊端:先对降低了效率,因为同步外的线程都会判断同步锁,比较耗费资源
* 同步的前提:1.必须有两个或者以上的线程
* 2.必须是多个线程使用同一个锁
* 如何找问题:
* 1.明确哪些代码是多线程运行的代码
* 2.明确共享资源
* 3.明确多线程运行代码中哪些语句是操作共享数据的
* */
class Ticket1 implements Runnable{
private int num=1000;
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){//锁必须是一个对象,如果这里new Object()则使用的不是同一个锁
if(num>0){
System.out.println(Thread.currentThread().getName()+"-----"+(num--));
}
}
}
}
}
//锁也可以定义在方法上,同步函数的锁是this
/*
* 同步函数和同步代码块的区别
* 同步函数的锁是固定的this
* 同步代码块的锁是任意对象
* 建议使用同步代码块
* 静态的同步函数使用的锁是,该函数的字节码文件,可以通过getClass()获取
* */
//死锁:就是同步中嵌套着同步
/*
* 死锁的条件:
* 1.两个或另个以上的线程
* 2.某一个线程拿到锁后,还想去拿第二把锁,即锁的嵌套
* 以下提供一个死锁的事例
* */
class Test1 implements Runnable{
private boolean flag;
private Object lock1 = new Object();
private Object lock2 = new Object();
Test1(boolean flag){
this.flag=flag;
}
public void run(){
if(flag){
synchronized(lock1){
System.out.println(Thread.currentThread().getName()+"---取得了lock1");
synchronized(lock2){
System.out.println(Thread.currentThread().getName()+"--是否取得lock2");
}
}
}else{
synchronized(lock2){
System.out.println(Thread.currentThread().getName()+"---取得了lock1");
synchronized(lock1){
System.out.println(Thread.currentThread().getName()+"--是否取得lock2");
}
}
}
}
}
//这样,线程里取得lock1之后,还想去lock2, 但是另一个线程不释放锁,互掐这样就死锁了
/*
* 线程间通讯:
* 其实就是多个线程在操作同一个资源
* */
/*
* 重点:等待/唤醒机制
* 涉及的方法:
* 1.wait():让线程处于冻结状态,被wait的线程会存储到线程池中
* 2.notify():唤醒线程池中的一个线程(随机的)
* 3.notifyAll():唤醒线程池中的所有线程
*
* 这些方法必须定义在同步中,因为要对持有锁的线程操作,只有同步中才有锁
* 必须明确到底操作的是那个锁上的线程
*
*
* 为什么操作线程的方法定义在Object类中?
* 因为这些方法是监视器的方法,监视器其实就是锁
* 锁可以是任意对象,任意的对象调用的方法一定定义在Object中
*
* wait和sleep的区别
* 1.wait() 可以指定时间,也可以不指定,sleep必须指定时间
* 2.在同步中,wait()释放执行权,并释放锁,sleep释放执行权,不释放锁
* */
/*
* 停止线程:
* 1.stop方法(已过时)
* 2.run方法结束
* 如何控制线程的任务?
* 任务中都会有循环结构,只要控制住循环,就可以结束任务
* 控制循环:通常用定义标记来完成
*
* 特殊情况:当线程处于冻结状态,就不会读取到标记,那么多线程就不会结束
* 当没有指定方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除
* 强制让线程恢复到运行状态中,这样就可以操作标记,让线程结束
* Thread类提供了该方法,interrupt()
* 但是强制动作会发生InerruptedException,需要处理
*
* */
//事例:
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
System.out.println(Thread.currentThread().getName()+"我要改标记"+e);
flag = false; //在异常中改变标记
}
System.out.println(Thread.currentThread().getName()+"我结束了");
}
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
for(int i=0;i<50;i++){
if(i==30){
t1.interrupt();
break;
}
System.out.println("main"+i);
}
System.out.println("over");
}
}
/*
* 线程的其他分类讲解
* 1.守护线程,需要再开启线程之前设置
* 守护线程可以简单理解为后台线程
* 当前台线程都结束时,后台线程自动结束
* 当线程都变成守护线程是,进程结束
* 可以理解为线程的嵌套 线程里的线程
*/
class Test3 implements Runnable
{
int count = 1;
public void run(){
while(true)
System.out.println(Thread.currentThread().getName()+"----"+(count++));
}
}
class ThreadDemo1
{
public static void main(String[] args){
Test3 test = new Test3();
Thread t = new Thread(test);
t.setDaemon(true);//这样t就是main主线程的守护线程
t.start();
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+"================="+i);
}
System.out.println("Game is over");
}
}
/* 2.join 方法,当A线程执行到了B线程的join 方法时,A就会等待B线程执行完,A才会执行
* join可以用来临时加入线程执行
* */
class Test2 implements Runnable
{
int count = 1;
public void run(){
for(int i=0;i<30;i++)
System.out.println(Thread.currentThread().getName()+"----"+(count++));
}
}
class ThreadJoin
{
public static void main(String[] args)throws Exception{
Test2 test = new Test2();
Thread t = new Thread(test);
t.start();
t.join();//join 方法一行定义在线程启动之后
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+"================="+i);
}
System.out.println("Game is over");
}
}
public class ThreadSummary {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}