进程是程序的一次执行过程,是系统运行程序的基本单位。而线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。
创建线程的方式
1.继承Thread类
定义一个继承自Thread的类,重写run()方法,创建这个类的实例然后调用start()方法启动新线程。
public class MyThread extends Thread { @Override public void run() { for (int i=1;i<=5;i++){ System.out.println("MyThread的线程输出"+i); } } }
public class Test1 { public static void main(String[] args) { Thread t= new MyThread(); t.start(); for (int i=1;i<=5;i++){ System.out.println("主线程的输出"+i); } } }
2.定义Runnable接口
定义一个实现Runnable接口的类,重写run()方法,然后创建Thread实例时将其作为target传入,并调用start()。
public class MyRunnble implements Runnable { @Override public void run() { for (int i=1;i<=5;i++){ System.out.println("MyThread的线程输出"+i); } } }
public class Test1 { public static void main(String[] args) { Runnable target= new MyRunnble(); new Thread(target).start(); // new Thread(target)得到线程对象 for (int i=1;i<=5;i++){ System.out.println("主线程的输出"+i); } } }
3.利用Callable接口,FutureTask类实现
此方法可返回线程执行结果
public class CallableAndFuturetask implements Callable<String>{ private int n; public CallableAndFuturetask(int n){ this.n =n; } @Override public String call() throws Exception{ int sum = 0; for(int i =1;i<=n;i++){ sum+=i; } return "线程求出的和是"+sum; } }
public class Test1 { public static void main(String[] args) throws Exception { Callable<String> call = new CallableAndFuturetask(100); FutureTask<String> f1 = new FutureTask<>(call); new Thread(f1).start(); String rs = f1.get(); System.out.println(rs); } }
Thread提供了很多与线程操作相关的方法
public class MyThread extends Thread { @Override public void run() { Thread t = Thread.currentThread(); for (int i=1;i<=5;i++){ System.out.println(t.getName()+"输出"+i); } } }
public class Test1 { public static void main(String[] args) { Thread t1 = new MyThread(); t1.setName("1号线程");//为线程赋名字 t1.start(); System.out.println(t1.getName()); Thread t2 = new MyThread(); t2.setName("2号线程");//为线程赋名字 t2.start(); System.out.println(t2.getName()); //获得子线程的名字 Thread m = Thread.currentThread(); m.setName("最大的线程");//为线程赋名字 System.out.println(m.getName()); //获得主线程的名字 for (int i =1;i<=5;i++){ System.out.println(m.getName()+"输出"+i); } } }
如何保证线程安全
1.同步代码块:把访问资源的核心代码上锁,每次只允许一个线程加锁后进入,以保证此线程的安全;
synchronized (同步锁){
访问共享资源的核心代码
}
实例方法使用this关键字进行加锁,静态方法使用类名.class作为锁对象
例如为银行取钱问题的核心代码加锁
package XianchengSaft; public class Account { private String IdCard; private double balance; public Account(){ } public Account(String idCard, double balance) { IdCard = idCard; this.balance = balance; } public String getIdCard() { return IdCard; } public void setIdCard(String idCard) { IdCard = idCard; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public static void test(){ synchronized (Account.class) { } } public void DrawMoney(double money){ String name = Thread.currentThread().getName(); synchronized (this) { if (this.balance>=money){ System.out.println(name + "来取"+money+"成功"); this.balance -= money; System.out.println(name + "取钱后剩余"+this.balance); }else{ System.out.println(name + "来取钱,余额不足"); } } } @Override public String toString() { return "Account{" + "IdCard='" + IdCard + '\'' + ", balance=" + balance + '}'; } }
2.同步方法:同步方法锁的范围更大,不利于程序的性能
修饰符 synchronized 返回值类型 方法名称(形参列表){
操作共享资源的核心代码
}
package XianchengSaft; public class Account { private String IdCard; private double balance; public Account() { } public Account(String idCard, double balance) { IdCard = idCard; this.balance = balance; } public String getIdCard() { return IdCard; } public void setIdCard(String idCard) { IdCard = idCard; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public static void test() { synchronized (Account.class) { } } public synchronized void DrawMoney(double money) { String name = Thread.currentThread().getName(); if (this.balance >= money) { System.out.println(name + "来取" + money + "成功"); this.balance -= money; System.out.println(name + "取钱后剩余" + this.balance); } else { System.out.println(name + "来取钱,余额不足"); } } @Override public String toString() { return "Account{" + "IdCard='" + IdCard + '\'' + ", balance=" + balance + '}'; } }
3.Lock锁
package XianchengSaft; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Account { private String IdCard; private double balance; private final Lock lk =new ReentrantLock(); public Account() { } public Account(String idCard, double balance) { IdCard = idCard; this.balance = balance; } public String getIdCard() { return IdCard; } public void setIdCard(String idCard) { IdCard = idCard; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public static void test() { synchronized (Account.class) { } } public void DrawMoney(double money) { String name = Thread.currentThread().getName(); try { lk.lock(); if (this.balance >= money) { System.out.println(name + "来取" + money + "成功"); this.balance -= money; System.out.println(name + "取钱后剩余" + this.balance); } else { System.out.println(name + "来取钱,余额不足"); } } catch (Exception e) { e.printStackTrace(); } finally { lk.unlock(); } } @Override public String toString() { return "Account{" + "IdCard='" + IdCard + '\'' + ", balance=" + balance + '}'; } }
synchronized 是什么?有什么用?
synchronized是java中的关键字,可以保证多个线程访问资源的同步性。
synchronized修饰实例方法
synchronized void method() {
//业务代码
}
synchronized修饰静态方法
synchronized static void method() {
//业务代码
}
synchronized修饰代码块
synchronized(this) {
//业务代码
}
volatile关键字作用
- 保证变量的可见性。如果被volatile关键字修饰的变量被一个线程修改的话,其他线程也能立马获取到变量修改后的值。
- 禁止指令重新排序。指令排序就是编译器和处理器为了提高效率对程序优化的手段,可保证输出正确的结果,但不能保证操作顺序与代码顺序一致,因此会在多线程中出现问题,禁止了指令排序后可防止出现这一问题。
公平锁和非公平锁的区别
公平锁:锁被释放后,先申请的线程先得到锁。
非公平锁:锁被释放后,后申请的线程可能先得到锁,得到锁的机制是随机的或是按照优先级排列的。