------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
黑马程序员 java 基础 毕向东 面向对象 多线程
1线程介绍
* 线程是一个执行中的程序* 每一个进程执行都有一个执行顺序,该顺序是一个执行路径
* 或者叫一个控制单元
*
* 线程 才是进程中独立的控制单元
* 线程在控制着进程的执行
* 一个进程中至少有一个线程
*
* java 虚拟机启动时会有一个进程叫做java.ext
* 该进程中至少有一个进程,负责执行
* 这个线程运行的代码存在于main方法中
* 该线程称为主线程
*
* 扩展:更详细是说
* jvm启动不止一个线程,还有垃圾回收机制的线程
2 线程实现 继承Thread
package ThreadH;
/*
* 线程是一个执行中的程序
* 每一个进程执行都有一个执行顺序,该顺序是一个执行路径
* 或者叫一个控制单元
*
* 线程 才是进程中独立的控制单元
* 线程在控制着进程的执行
* 一个进程中至少有一个线程
*
* java 虚拟机启动时会有一个进程叫做java.ext
* 该进程中至少有一个进程,负责执行
* 这个线程运行的代码存在于main方法中
* 该线程称为主线程
*
* 扩展:更详细是说
* jvm启动不止一个线程,还有垃圾回收机制的线程
*
*
* 如何自定义线程呢
* 通过对api查阅,java以及封装了线程这类事物的描述即Thread类
* 继承重新run即可
* 步骤:
* 1:继承Thread
* 2;复写run方法
* 目的:将自定义的代码存储在run方法中,让线程运行
* 3:调用线程的start方法;有俩个作用,1启动线程2调用run方法
* 结果并不相同,而且main和新线程并不是同时执行,
* CPU实际上在线程之间快速切换,cpu 执行到谁,谁运行
* 给人看起来是在同时执行的
* 我们可以形象的多线程的运行行为在互相抢夺CPU的执行权
* 执行多长时间,CPU说了算
*
* 为什么要覆盖run方法呢
* Thread 类用于描述线程。
* 该类就定义了一个功能,用于存储线程要运行的代码,该
* 存储功能就是run方法
* 也就是说 Thread 的run方法 用于存储线程要执行的代码
* 问题来了:
* */
public class ThreadDemo{
public static void main(String args[]) {
Demo d=new Demo();
d.start();
//开启线程并执行该线程的run方法
//d.run();仅仅是对象的调用
for(int i=0;i<100;i++)
System.out.println("main run");
}
}
class Demo extends Thread{
public void run(){
for(int i=0;i<100;i++)
System.out.println("demo run");
}
}
3 线程名字
package ThreadH;
/*
* 线程都有自己默认的名字
*THread-编号 从零开始
*static Thread currenThread()获取当前线程的对象
*getName() 获取线程的名称
*setName() 或者使用构造函数 设置名称*/
public class ThreadTestT{
public static void main(String[] args) {
TestT t=new TestT("t1");
TestT t2=new TestT("t2");
t.start();
t2.start();
for (int i = 0; i < 60; i++) {
System.out.println("main run +"+i);
}
}
}
class TestT extends Thread{
//private String name;
public TestT(String name) {
super(name);
//this.name=name;
}
public void run() {
for (int i = 0; i < 60; i++) {
//System.out.println(name+"test run +"+i);
System.out.println((Thread.currentThread()==this)+this.getName()+"test run +"+i);
}
}
}
4 卖票程序 中 体现Runnable 实现方式 and 实现线程同步 and 线程同步锁 this
package ThreadH;
/*需求:简单的卖票程序
* 多个窗口卖票
* 创建线程的第二中方式:
* 1;定义类实现Runnable接口,
* 2:覆盖Runnable接口中的run方法
* 将线程要运行的代码存放在run方法中
*
* 3:通过Thread类建立线程对象
* 4:将Runnable接口的子类对象作为实际参数传递给Thread类的构造函
* why?因为自定义的 的run方法所属的对象是Runnable接口的子类对象
* 所以要让线程去指定对象的run方法,就必须明确该run方法所属对象
* 5:调用Thread类的start方格,开启线程并执行Runnable中的run方法
*
* 实现方式和继承方式的区别;
*关键的部分:
*避免了单继承的局限性
*建议使用 的多线程实现方式
*
*区别:
*继承Thread线程代码存放在Thread的子类run方法中
*
*实现Runnable 线程代码存放在接口子类的run方法
*
*
*通过分析,发现打印出0 -1 -2 等错票
*多线程的运行出现了安全问题
*
*问题产生的原因:
*当多条语句在操作线程共享数据时,一个线程对多条语句只执行了一部分,还木有执行完,
*另一个线程进入参与执行,导致共享数据 的错误
*
*解决办法:
*对多条操作共享数据 的语句,只能让一个线程执行完,在执行过程中,
*其他线程不可以参与执行
*
*java 对于多线程的安全问题提供了专业的解决方式
*就是同步代码块
*synchronized(对象){
*需要被同步的代码
*}
*
*对象如同锁,持有锁的线程可以在同步中执行
*没有持有锁 的线程即使获取CPU 的执行权,也进不去,因为没获取锁
*火车上的卫生间------------经典同步
*
*使用同步的前提:
*1:必须有两个或两个以上的线程
*2:必须是多个线程使用同一个锁
*必须保证同步中只有一个线程在运行
*
*好处:解决了多线程的安全问题
*弊端:多个线程每一次都需要判断,消耗了资源,
*
*
**同步函数用的是哪个锁呢???
* --------this-----
* 函数需要被对象调用,函数都有所属对象的引用
* 所以同步函数使用的锁是this
*
* 使用该程序进行验证:
* 一个线程在同步代码块中
* 一个线程在同步函数中
* 都在执行卖票动作
* Thread-1shwo sale:0 出现错票
* */
public class TicketThread {
public static void main(String[] args) {
//Ticket ticket1=new Ticket();
//Ticket ticket2=new Ticket();
//Ticket ticket3=new Ticket();
//Ticket ticket4=new Ticket();
//ticket1.start();
//ticket2.start();
//ticket3.start();
//ticket4.start();
//ticket1.start();
//ticket1.start();
//ticket1.start();
//ticket1.start();
Ticket ticket=new Ticket();
//构造方法:解决Thread(Runnable runnable)
Thread t1=new Thread(ticket);
Thread t2=new Thread(ticket);
Thread t3=new Thread(ticket);
Thread t4=new Thread(ticket);
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket.flag=false;
t2.start();
//t3.start();
//t4.start();
}
}
//class Ticket extends Thread{
//private static int tick=100;
//public void run() {
//while(true){
//if(tick>0){
//System.out.println(currentThread().getName()+"sale:"+tick--);
//}
//}
//}
//}
class Ticket implements Runnable{
private static int tick=400;
Object obj=new Object();
boolean flag=true;
public void run() {
if(flag){
while(true){
//synchronized(obj){//同步代码块
synchronized(this){//同步代码块
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"run sale:"+tick--);
}
}
}
}else{
while(true)
show();
}
}
synchronized void show (){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"shwo sale:"+tick--);
}
}
}
//class Ticket implements Runnable{
//private static int tick=400;
//Object obj=new Object();
//public void run() {
//while(true){
//show();
//}
//}
//synchronized void show (){
//if(tick>0){
//try {
//Thread.sleep(10);
//} catch (InterruptedException e) {
//e.printStackTrace();
//}
//System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
//}
//}
//}6
5卖票程序中 静态代码块 同步
package ThreadH;
/*需求:简单的卖票程序
* 多个窗口卖票
* 创建线程的第二中方式:
* 1;定义类实现Runnable接口,
* 2:覆盖Runnable接口中的run方法
* 将线程要运行的代码存放在run方法中
*
* 3:通过Thread类建立线程对象
* 4:将Runnable接口的子类对象作为实际参数传递给Thread类的构造函
* why?因为自定义的 的run方法所属的对象是Runnable接口的子类对象
* 所以要让线程去指定对象的run方法,就必须明确该run方法所属对象
* 5:调用Thread类的start方格,开启线程并执行Runnable中的run方法
*
* 实现方式和继承方式的区别;
*关键的部分:
*避免了单继承的局限性
*建议使用 的多线程实现方式
*
*区别:
*继承Thread线程代码存放在Thread的子类run方法中
*
*实现Runnable 线程代码存放在接口子类的run方法
*
*
*通过分析,发现打印出0 -1 -2 等错票
*多线程的运行出现了安全问题
*
*问题产生的原因:
*当多条语句在操作线程共享数据时,一个线程对多条语句只执行了一部分,还木有执行完,
*另一个线程进入参与执行,导致共享数据 的错误
*
*解决办法:
*对多条操作共享数据 的语句,只能让一个线程执行完,在执行过程中,
*其他线程不可以参与执行
*
*java 对于多线程的安全问题提供了专业的解决方式
*就是同步代码块
*synchronized(对象){
*需要被同步的代码
*}
*
*对象如同锁,持有锁的线程可以在同步中执行
*没有持有锁 的线程即使获取CPU 的执行权,也进不去,因为没获取锁
*火车上的卫生间------------经典同步
*
*使用同步的前提:
*1:必须有两个或两个以上的线程
*2:必须是多个线程使用同一个锁
*必须保证同步中只有一个线程在运行
*
*好处:解决了多线程的安全问题
*弊端:多个线程每一次都需要判断,消耗了资源,
*
*
**同步函数用的是哪个锁呢???
* --------this-----
* 函数需要被对象调用,函数都有所属对象的引用
* 所以同步函数使用的锁是this
*
* 使用该程序进行验证:
* 一个线程在同步代码块中
* 一个线程在同步函数中
* 都在执行卖票动作
* Thread-1shwo sale:0 出现错票
* */
public class StaticTicketThread {
public static void main(String[] args) {
//Ticket ticket1=new Ticket();
//Ticket ticket2=new Ticket();
//Ticket ticket3=new Ticket();
//Ticket ticket4=new Ticket();
//ticket1.start();
//ticket2.start();
//ticket3.start();
//ticket4.start();
//ticket1.start();
//ticket1.start();
//ticket1.start();
//ticket1.start();
TicketSta ticket=new TicketSta();
//构造方法:解决Thread(Runnable runnable)
Thread t1=new Thread(ticket);
Thread t2=new Thread(ticket);
Thread t3=new Thread(ticket);
Thread t4=new Thread(ticket);
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket.flag=false;
t2.start();
//t3.start();
//t4.start();
}
}
//class Ticket extends Thread{
//private static int tick=100;
//public void run() {
//while(true){
//if(tick>0){
//System.out.println(currentThread().getName()+"sale:"+tick--);
//}
//}
//}
//}
class TicketSta implements Runnable{
private static int tick=400;
Object obj=new Object();
boolean flag=true;
public void run() {
if(flag){
while(true){
//synchronized(obj){//同步代码块
//synchronized(this){//同步代码块
synchronized(TicketSta.class){//同步代码块
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"run sale:"+tick--);
}
}
}
}else{
while(true)
show();
}
}
//static synchronized void show (){
//如果是静态方法,使用的锁不再是this,因为静态方法不可以定义this
//类进入内存,生成对应类的字节码对象
//静态进内存,内存中没有本类对象,但一定有该类对应的字节码文件对象
//类名.class 该对象的类型是Class
//静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class 在内存中唯一
static synchronized void show (){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"shwo sale:"+tick--);
}
}
}
//class Ticket implements Runnable{
//private static int tick=400;
//Object obj=new Object();
//public void run() {
//while(true){
//show();
//}
//}
//synchronized void show (){
//if(tick>0){
//try {
//Thread.sleep(10);
//} catch (InterruptedException e) {
//e.printStackTrace();
//}
//System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
//}
//}
//}
6银行程序 模拟多线程 操作
package ThreadH;
import sun.launcher.resources.launcher;
/*
*银行有一个金库
*有两个储户,分别存300员,每次存100,存3次
*目的:该程序是否有安全问题?
*如果有,如何解决
*如何找到问题:
*1:明确哪些代码是多线程运行代码
*for (int i = 0; i < 3; i++) {
bank.add(100);
}
*2:明确共享数据 private int sum;
private Bank bank=new Bank();
*3:明确多线程运行代码中是操作共享数据的
*
sum+=n;
System.out.println("sum="+sum);
*
*
*同步的两种表现形式
*1:同步代码块 synchronized(Object o) { 同步代码}
*2:同步函数 将 synchronized 作为函数的修饰符
*
*
*同步函数用的是哪个锁呢???
* --------this-----
* 函数需要被对象调用,函数都有所属对象的引用
* 所以同步函数使用的锁是this
* */
public class BankDemo {
public static void main(String[] args) {
Cus cus=new Cus();
Thread thread1=new Thread(cus);
Thread thread2=new Thread(cus);
thread1.start();
thread2.start();
}
}
class Bank{
private int sum;
//public void add(int n) {
//synchronized (this) {
//sum+=n;
//try {
//Thread.sleep(10);
//} catch (InterruptedException e) {
//e.printStackTrace();
//}
//System.out.println("sum="+sum);
//}
//
//}
public synchronized void add(int n) {
//synchronized (this) {
sum+=n;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sum="+sum);
//}
}
}
class Cus implements Runnable{
private Bank bank=new Bank();
public void run() {
for (int i = 0; i < 3; i++) {
bank.add(100);
}
}
}
7单例模式中多线程 的使用 饿汉式 懒汉式
package ThreadH;
/*
* 单例设计模式
*
* 1:饿汉式
* 2:懒汉式
* -------------------------------------------------------
* 饿汉式:
* class ClassA {
* private static final ClassA c=new ClassA();
* private ClassA(){
* }
* public static ClassA getInstance(){
* return c;
* }
* }
*
* -------------------------------------------------------
* 懒汉式
* class ClassA {
* private static ClassA c=null;
* private ClassA(){
* }
* public static ClassA getInstance(){
* if(c==null){
* c=new ClassA();
* 为空才实例化对象,所以叫做懒懒
* 也教师延迟加载,对象被延迟了
* }
* return c;
* }
* }
*
* 懒汉式与饿汉式的区别:
*
* */
public class SingleDemo {
public static void main(String[] args) {
}
}
class Single {
private static Single c=null;
private Single(){
}
public static Single getInstance(){
// public static synchronized Single getInstance(){
//虽然可以解决同步问题,但会低效
//用双重判断可以提高效率
/*
* 分析执行过程
* Thread 1执行到if(c==null)为真,
* 执行synchronized (Single.class) {}
* 获取了锁,其他线程可以可以执行到该行之前,但会在此次等待
* Thread 判断if(c==null) 为真,实例化 c
* 执行完同步代码块后退出,其他线程进入同步代码块,
* 执行if(c==null) 为假,退出
* 当以后的线程在实例化时,第一行的if(c==null)判断不通过
* 就不会再执行同步代码块
*
* 在以后的实例化过程中提高了下效率
*
* 最好是选择是使用饿汉式
* */
if(c==null){
synchronized (Single.class) {
if(c==null){
c=new Single();
}
}
}
return c;
}
}
8思索死锁
例子1
package ThreadH;
/*思索死锁
* 例如筷子
*
* 同步中嵌套同步
* */
public class DeadLockTicketThread {
public static void main(String[] args) {
TicketDead ticket=new TicketDead();
//构造方法:解决Thread(Runnable runnable)
Thread t1=new Thread(ticket);
Thread t2=new Thread(ticket);
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket.flag=false;
t2.start();
}
}
class TicketDead implements Runnable{
private static int tick=1000;
Object obj=new Object();
boolean flag=true;
public void run() {
if(flag){
while(true){
synchronized(obj){//同步代码块
show();
}
}
}else{
while(true)
show();
}
}
synchronized void show (){
synchronized(obj){//同步代码块
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"run sale:"+tick--);
}
}
}
}
//}
例子2
package ThreadH;
public class DeadLockTest{
public static void main(String args[]) {
Thread thread1=new Thread(new TestDe(true));
Thread thread2=new Thread(new TestDe(false));
thread1.start();
thread2.start();
}
}
class TestDe implements Runnable {
private boolean flag;
public TestDe(boolean flag) {
this.flag= flag;
}
@Override
public void run() {
if (flag) {
while(true){
synchronized (MyLock.locka) {
System.out.println("if locka");
synchronized (MyLock.lockb) {
System.out.println("else lockb");
}
}
}
}else {
while(true){
synchronized (MyLock.lockb) {
System.out.println("else lockb");
synchronized (MyLock.locka) {
System.out.println("else locka");
}
}
}//while
}
}
}
class MyLock{
static Object locka=new Object();
static Object lockb=new Object();
}