进程:正在进行中的程序;每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:进程中负责程序执行的控制单元(执行路径),线程在控制着进程的执行。
通过对api的查找,java已经提供了对线程这类事物的描述,即Thread类。
例1
class Demo extends Thread{
public void run(){
for(int x =0;x<6;x++){
System.out.println("demo run"+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();//创建了一个线程
d.start();//开启线程并执行该线程的run方法
//d.run();//仅仅对象调用方法,而线程创建了并未运行。
for(int x =0;x<5;x++){
System.out.println("Hello"+x);
}
}
}
运行结果:
创建线程的目的就是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行,而运行的指定代码就是这个执行路径的任务。
为何要重写run方法:
Thread类用于描述线程,该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。
多线程运行状态:
例2
class Demo extends Thread{
Demo(String name){
super(name);
}
public void run(){
for(int x =0;x<6;x++){
System.out.println((Thread.currentThread()==this)+"..."+this.getName()+".run.."+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo("one");//创建了一个线程
Demo d1= new Demo("two");
d.start();
d1.start();
for(int x =0;x<5;x++){
System.out.println("Hello"+x);
}
}
}
PS:
1. 可通过Thread的getName()方法获取线程的名称,名称格式如下:Thread-编号(0开始);
2. Thread创建时就已经命名了;
3. currentThread();获取当前线程对象;
4. 设置线程名称:setName或者构造函数。
步骤:
例3
class Ticket1 implements Runnable{
private int tick = 8;
public void run(){
while(true){
if(tick>0)
System.out.println(Thread.currentThread().getName()+
"...sale.."+tick--);
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
运行结果:
实现方式和继承方式的区别:
代码:
class Ticket1 implements Runnable{
private int tick = 10;
public void run(){
while(true){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"...sale.."+tick--);
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
运行结果:
打印出tick小于等于0的结果,
问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
采用同步代码块:
synchronized(对象){
需要被同步的代码;
}
对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权也进不去,因为没有获取锁。
使用前提必须有多个线程并使用同一个锁。
修改后的代码:
class Ticket1 implements Runnable{
private int tick = 10;
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"...sale.."+tick--);
}
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
同步代码块的弊端:多个线程需要判断锁,较为消耗资源,会降低程
序的运行效率。
如何找问题:
例4:
class Bank{
private int sum;
public void add(int n){
sum += n;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sum="+sum);
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run() {
for(int x=0;x<3;x++){
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t1.start();
t2.start();
t3.start();
}
}
运行结果:
修改后:
class Bank{
private int sum;
Object obj = new Object();
public synchronized void add(int n){//同步函数
//synchronized(obj){
sum += n;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sum="+sum);
//}
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run() {
for(int x=0;x<3;x++){
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t1.start();
t2.start();
t3.start();
}
}
同步的两种表现形式:同步代码块和同步函数。
Ps:
1. 同步函数的锁是固定的this;
2. 同步代码块的锁是任意的对象。
由于同步函数的锁是固定的this,同步代码块是锁的任意的对象,如果同步函数和同步代码块都使用this作为锁,就可以实现同步。
class Ticket implements Runnable{
private int tick = 10;
boolean flag = true;
//Object obj = new Object();
public void run(){
if(flag){
while(true){
synchronized(this){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"...code.."+tick--);
}
}
}
}else{
while(true)
show();
}
}
public synchronized void show(){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"..function.."+tick--);
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);//线程休眠目的是为了使t1真正启动后,flag才设置为false;
} catch (InterruptedException e) {
e.printStackTrace();
}
t.flag=false;
t2.start();
}
}
运行结果:
如果同步函数被静态修饰后,使用的锁不是this,而是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示。
class Ticket implements Runnable{
private static int tick = 10;
boolean flag = true;
public void run(){
if(flag){
while(true){
synchronized(Ticket.class){//this.getClass()
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"...code.."+tick--);
}
}
}
}else{
while(true)
show();
}
}
public static synchronized void show(){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"..function.."+tick--);
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);//线程休眠目的是为了使t1真正启动后,flag才设置为false;
} catch (InterruptedException e) {
e.printStackTrace();
}
t.flag=false;
t2.start();
}
}
1. 饿汉式
class Single{
private static final Single s = new Single();
private Single(){}
public static Single getInstance(){
return s;
}
}
饿汉式不存在安全问题,因为不存在多个线程共同操作数据的情况。
2. 懒汉式
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s==null){
synchronized(Single.class){
if(s==null)
s =new Single();
}
}
return s;
}
}
懒汉式存在安全问题,可以使用同步函数解决。
方式一:
public static synchronized Single getInstance(){
if(s==null)
s =new Single();
return s;
}
该方式使用效率较低,每次均需要判断;
方法二:
public static Single getInstance(){
if(s==null){
synchronized(Single.class){
if(s==null)
s =new Single();
}
}
return s;
}
原因在于任何一个线程在执行到第一个if判断语句时,如果Single对象已经创建,则直接获取即可,而不用判断是否能够获取锁,相对于上面使用同步函数的方法就提升了效率。如果当前线程发现Single对象尚未创建,则
再判断是否能够获取锁。
同步嵌套的死锁
class Ticket implements Runnable{
private static int tick = 10;
boolean flag = true;
Object obj = new Object();
public void run(){
if(flag){
while(true){
synchronized(obj){//this.getClass()
show();
}
}
}else{
while(true)
show();
}
}
public synchronized void show(){
synchronized(obj){
if(tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"..function.."+tick--);
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try {
Thread.sleep(10);//线程休眠目的是为了使t1真正启动后,flag才设置为false;
} catch (InterruptedException e) {
e.printStackTrace();
}
t.flag=false;
t2.start();
}
}