/*
* 多线程的运行根据cpu的切换完成的。怎么切换cpu说了算
* 所以多线程运行有一个随机性(cpu的快速切换造成的)。
*
* jvm中的多线程。
* 至少有两个线程:一个是负责自定义代码运行的。这个从main方法开始执行的线程称之为主线程。
* 一个是负责垃圾回收的。
*
*通过实验,会发现每次结果都不一定相同,因为随机性造成的。
*
*而且每一个线程都有运行的代码内容。这个称之为线程的任务。
*之所以创建一个线程就是为了去运行指定的任务代码。
*
*而线程的任务都封装在特定的区域中。
*比如:
*主线程运行的任务都定义在main方法中
*垃圾回收线程在收垃圾都会运行finalize方法。
*/
/*建立线程:第一种方式
* 查看API文档java.lang.Thread类。
* 该类的描述中有创建线程的两种方式:
* 一.继承Thread类
* 1.继承Thread类。
* 2.覆盖run方法。
* 3.创建子类对象就是创建线程对象。
* 4.调用Thread类中的start方法就可以执行线程。并会调用run方法。
*
* start()开启线程后,都会执行run方法。说明run方法中存储的是线程要运行的代码。
* 所以,自定义线程的任务代码都存储在run方法中
*
* 调用start和调用run方法的区别?
* 调用start会开启线程,让开启的线程去执行run方法中的线程任务。
* 直接调用run方法,线程并未开启,去执行run方法的只有主线程(main)
*/
class Demo2 extends Thread{
private String name;
Demo2(String name) //throws Exception
{
// if(!(name instanceof String))
// {
// throw new RuntimeException(name+"错误");
// }
this.name=name;
}
void show()
{
for(int x=0;x<10;x++)
{
System.out.println(name+"..."+x);
}
}
public void run() //线程要运行的代码
{
show();
}
}
public class FinalizeDemo2 {
* 查看API文档java.lang.Thread类。
* 该类的描述中有创建线程的两种方式:
* 一.继承Thread类
* 1.继承Thread类。
* 2.覆盖run方法。
* 3.创建子类对象就是创建线程对象。
* 4.调用Thread类中的start方法就可以执行线程。并会调用run方法。
*
* start()开启线程后,都会执行run方法。说明run方法中存储的是线程要运行的代码。
* 所以,自定义线程的任务代码都存储在run方法中
*
* 调用start和调用run方法的区别?
* 调用start会开启线程,让开启的线程去执行run方法中的线程任务。
* 直接调用run方法,线程并未开启,去执行run方法的只有主线程(main)
*/
class Demo2 extends Thread{
private String name;
Demo2(String name) //throws Exception
{
// if(!(name instanceof String))
// {
// throw new RuntimeException(name+"错误");
// }
this.name=name;
}
void show()
{
for(int x=0;x<10;x++)
{
System.out.println(name+"..."+x);
}
}
public void run() //线程要运行的代码
{
show();
}
}
public class FinalizeDemo2 {
public static void main(String[] args) {
Demo2 d1 = new Demo2("jason"); //创建Thread子类对象就是创建线程对象
Demo2 d2 = new Demo2("swift");
d1.start(); //开始线程的开关:1.开启线程。2.让jvm虚拟机调用run方法
d2.start();
}
Demo2 d1 = new Demo2("jason"); //创建Thread子类对象就是创建线程对象
Demo2 d2 = new Demo2("swift");
d1.start(); //开始线程的开关:1.开启线程。2.让jvm虚拟机调用run方法
d2.start();
}
}
/*
建立线程的第二种方式。实现Runnable接口。
1,定义一个类实现Runnable。
2.覆盖Runnable接口中的run方法,将线程要运行的任务代码存储到该方法中。
3.通过Thread类创建线程对象,并将实现了Runnable接口的对象作为Thread类的构造函数的参数进行传递。
4。调用Thread类的start方法,开启线程。
建立线程的第二种方式。实现Runnable接口。
1,定义一个类实现Runnable。
2.覆盖Runnable接口中的run方法,将线程要运行的任务代码存储到该方法中。
3.通过Thread类创建线程对象,并将实现了Runnable接口的对象作为Thread类的构造函数的参数进行传递。
4。调用Thread类的start方法,开启线程。
实现Runnable接口的好处:
1.避免了继承Thread类的单继承的局限性。
2.Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
3.Runnable接口出现,降低了线程对象和线程任务的耦合性。
!!!所以,以后创建线程都使用第二种方式。
*/
class SaleTicket2 implements Runnable//降低了线程对象和线程任务的耦合性
{
private int tickets=100;
public void run()
{
while(true)
{
if(tickets>0)
{
System.out.println(Thread.currentThread().getName()+"..普快.."+tickets--);
}
}
}
}
//class SaleTicket2_1 implements Runnable//任务代码和线程子类解耦
//{
// private int tickets=100;
// public void run()
// {
// while(true)
// {
// if(tickets>0)
// {
// System.out.println(Thread.currentThread().getName()+"..高铁.."+tickets--);
// }
// }
// }
//}
public class TicketDemo2
{
public static void main(String[] args) {
SaleTicket s = new SaleTicket();
// SaleTicket2_1 s2= new SaleTicket2_1();
Thread t1 = new Thread(s); //多个线程处理一个共享资源(run方法)
Thread t2 = new Thread(s);
// Thread t3 = new Thread(s2);
// Thread t4 = new Thread(s2);
t1.start();
t2.start();
// t3.start();
// t4.start();
}
}
1.避免了继承Thread类的单继承的局限性。
2.Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
3.Runnable接口出现,降低了线程对象和线程任务的耦合性。
!!!所以,以后创建线程都使用第二种方式。
*/
class SaleTicket2 implements Runnable//降低了线程对象和线程任务的耦合性
{
private int tickets=100;
public void run()
{
while(true)
{
if(tickets>0)
{
System.out.println(Thread.currentThread().getName()+"..普快.."+tickets--);
}
}
}
}
//class SaleTicket2_1 implements Runnable//任务代码和线程子类解耦
//{
// private int tickets=100;
// public void run()
// {
// while(true)
// {
// if(tickets>0)
// {
// System.out.println(Thread.currentThread().getName()+"..高铁.."+tickets--);
// }
// }
// }
//}
public class TicketDemo2
{
public static void main(String[] args) {
SaleTicket s = new SaleTicket();
// SaleTicket2_1 s2= new SaleTicket2_1();
Thread t1 = new Thread(s); //多个线程处理一个共享资源(run方法)
Thread t2 = new Thread(s);
// Thread t3 = new Thread(s2);
// Thread t4 = new Thread(s2);
t1.start();
t2.start();
// t3.start();
// t4.start();
}
}
/*
如何停止线程?
原理:让run方法结束。
线程任务通常都有循环。因为开启线程就是为了执行需要一些时间的代码。
只要控制住循环,就可以结束run方法,就可以停止线程。
控制循环弄个标记即可。定义变量
多线程的安全问题
运行结果会出现:0,-1,-2等
产生的原因:
1.线程任务中有处理到共享的数据
2.线程任务中有多条对共享数据的操作
一个线程在操作共享线程的过程中,其他线程参与了运算,造成了数据的错误。
运行结果会出现:0,-1,-2等
产生的原因:
1.线程任务中有处理到共享的数据
2.线程任务中有多条对共享数据的操作
一个线程在操作共享线程的过程中,其他线程参与了运算,造成了数据的错误。
解决思想:
只要保证多条操作共享数据的代码在某一时间段,被一条线程所执行,在执行期间不允许其他线程参与运算。
只要保证多条操作共享数据的代码在某一时间段,被一条线程所执行,在执行期间不允许其他线程参与运算。
怎么能保证呢?
用到了同步代码块。只同步需要操作共享数据的代码块。
synchronized(对象)
{
需要被同步的代码块
}
同步在目前情况下保证了一次只能有一个线程在执行。其他线程进不来。
这就是同步的锁机制。解决了多线程的安全问题
用到了同步代码块。只同步需要操作共享数据的代码块。
synchronized(对象)
{
需要被同步的代码块
}
同步在目前情况下保证了一次只能有一个线程在执行。其他线程进不来。
这就是同步的锁机制。解决了多线程的安全问题
有可能出现一种情况:
多线程安全问题出现后,加入同步机制,安全问题依旧!
这时肯定是同步出了问题。
只要遵守同步的前提,就可以解决。
同步的前提:
多个线程在同步中必须使用同一个锁,这才是对多个线程同步。
多线程安全问题出现后,加入同步机制,安全问题依旧!
这时肯定是同步出了问题。
只要遵守同步的前提,就可以解决。
同步的前提:
多个线程在同步中必须使用同一个锁,这才是对多个线程同步。
/*
在ThreadTest6基础上,增加了同步函数。
在ThreadTest6基础上,增加了同步函数。
同步函数,就是在函数上加上了同步关键字进行修饰。
同步表现形式有两种:1.同步代码块 2.同步函数
同步表现形式有两种:1.同步代码块 2.同步函数
同步函数使用的锁是什么呢?函数需要被对象调用,哪个对象不确定,但是都用this来表示
同步函数使用的锁就是this
同步函数使用的锁就是this
如果同步函数被static修饰呢?
static方法随着类加载,这时不一定有该类的对象。但是一定有一个该类的字节码文件对象。
这个对象简单的表示方式是 类名.class Class是描述类和接口的类
这个对象简单的表示方式是 类名.class Class是描述类和接口的类
同步函数和同步代码块的区别:
同步代码块使用的任意的对象作为锁。
同步函数只能使用this作为锁。
如果说:一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。
但是,一个类中如果需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。
建议使用同步代码块
*/
class Bank7
{
private int sum;
Object obj = new Object();
public synchronized void add(int n)
{
sum=sum+n;
try
{
Thread.sleep(10);
}
catch(InterruptedException e)
{
}
System.out.println("sum="+sum);
}
}
class Customer7 implements Runnable
{
private Bank7 b = new Bank7();
public void run()
{
for(int x=0;x<3;x++)
{
b.add(100);
}
}
}
public class ThreadTest7 {
同步代码块使用的任意的对象作为锁。
同步函数只能使用this作为锁。
如果说:一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。
但是,一个类中如果需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。
建议使用同步代码块
*/
class Bank7
{
private int sum;
Object obj = new Object();
public synchronized void add(int n)
{
sum=sum+n;
try
{
Thread.sleep(10);
}
catch(InterruptedException e)
{
}
System.out.println("sum="+sum);
}
}
class Customer7 implements Runnable
{
private Bank7 b = new Bank7();
public void run()
{
for(int x=0;x<3;x++)
{
b.add(100);
}
}
}
public class ThreadTest7 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
}
}
/*
单例模式的并发访问
*/
//饿汉式 相对于多线程开发 安全!
//class Single{
// private static final Single SINGLE_INSTANCE = new Single();
// private Single(){}
// public static Single getInstance()
// {
// return SINGLE_INSTANCE;
// }
//}
//懒汉式(延迟加载模式)
/*
在多线程并发访问时,会出现线程安全问题。
加了同步就可以解决问题。无论是同步函数,还是同步代码块都行。
但是效率降低了。
可以通过if对单例对象的双重判断的形式。
*/
class Single{
private static Single s=null;
private Single(){}
// public static synchronized Single getInstance()
public static Single getInstance()
{
if(s==null){
synchronized(Single.class)//类的字节码文件对象
{
if(s==null)
{
s=new Single();
}
}
}
return s;
}
}
class Demo8 implements Runnable
{
public void run()
{
Single.getInstance();
}
}
单例模式的并发访问
*/
//饿汉式 相对于多线程开发 安全!
//class Single{
// private static final Single SINGLE_INSTANCE = new Single();
// private Single(){}
// public static Single getInstance()
// {
// return SINGLE_INSTANCE;
// }
//}
//懒汉式(延迟加载模式)
/*
在多线程并发访问时,会出现线程安全问题。
加了同步就可以解决问题。无论是同步函数,还是同步代码块都行。
但是效率降低了。
可以通过if对单例对象的双重判断的形式。
*/
class Single{
private static Single s=null;
private Single(){}
// public static synchronized Single getInstance()
public static Single getInstance()
{
if(s==null){
synchronized(Single.class)//类的字节码文件对象
{
if(s==null)
{
s=new Single();
}
}
}
return s;
}
}
class Demo8 implements Runnable
{
public void run()
{
Single.getInstance();
}
}
public class ThreadTest8 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
}
}