java多线程的两种实现
1.继承Thread类覆写run方法
public class ThreadClass extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public static void main(String[] args) {
ThreadClass tClass = new ThreadClass();
ThreadClass tClass2 = new ThreadClass();
tClass.start();
tClass2.start();
}
}
2.实现Runnable接口实现run方法
public class RunClass implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
public static void main(String[] args) {
RunClass rClass = new RunClass();
new Thread(rClass).start();
new Thread(rClass).start();
}
}
两种方法比较:
相比Thread,Runable
适合多个相同的程序代码的线程去处理同一个资源,可以避免java中的单继承的限制
对于还有网上的一些资料说Thread不能共享资源我不能理解下面是给出的例子
Thread:
public class ThreadClass extends Thread {
private int t = 5;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (t > 0) {
System.out.println(Thread.currentThread().getName() + ":" + t);
t--;
}
}
}
public static void main(String[] args) {
ThreadClass tClass = new ThreadClass();
new Thread(tClass).start();
new Thread(tClass).start();
}
}
Runable:
public class RunClass implements Runnable {
private int t = 5;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (t > 0) {
System.out.println(Thread.currentThread().getName() + ":" + t);
t--;
}
}
}
public static void main(String[] args) {
RunClass rClass = new RunClass();
new Thread(rClass).start();
new Thread(rClass).start();
}
}
多线程的一些基本方法
public class RunClass implements Runnable {
private int t = 5;
@Override
public synchronized void run() {// 同步方法(如果是静态方法,两个也是不能达到互斥的目的,因为静态的锁对象是类.class对象),或者同步代码块都行,使用同//步代码块时候注意使用同一对象锁,同步方法默认的锁对象就是this!
for (int i = 0; i < 10; i++) {
if (t > 0) {
System.out.println(Thread.currentThread().getName() + ":" + t);
try {
Thread.sleep(10000);// 线程休眠10秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("线程休眠被打断。");
e.printStackTrace();
}
if (i == 5) {
System.out.println("线程礼让了");
Thread.currentThread().yield();// 线程礼让
}
t--;
}
}
}
public static void main(String[] args) {
RunClass rClass = new RunClass();
Thread rt = new Thread(rClass);
System.out.println("线程是否启动:" + rt.isAlive());// isAlive判断线程是否启动
rt.setDaemon(true);// 设置后台运行
rt.setPriority(10);// 设置线程的优先级1~10如果不设置的话默认为5
rt.start();// start()启动线程
System.out.println("线程是否启动:" + rt.isAlive());
for (int i = 0; i < 30; i++) {
if (i > 5) {
try {
// rt.join();// 强制执行线程
Thread.sleep(1000);// 主线程休息1秒
rt.interrupt();// 线程将被打断,强制执行时不能被打断
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("主线程:" + i);
}
}
}
wait和sleep区别
1、这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。
Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
3、使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
public static void main(String[] args) {
ThreadClass tClass = new ThreadClass();
new Thread(tClass).start();
new Thread(tClass).start();
synchronized (tClass) {
for (int i = 0; i < 30; i++) {
try {
tClass.wait(1000);//wait必须在对象的锁内,而且wait和notify必须操作的是同一个对象的锁不然报错
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("ddd");
}
}
}
线程的互斥于同步
public class MutualClass implements Runnable {
private int t = 5;
InnerClass iClass = new InnerClass();
static class InnerClass {//因为成员内部类不允许声明静态方法所以只好改为静态内部类了
private static String j = "j"; // 此处也可以作为锁对象,但是不可将变量放到run中去因为每次执行都会创建一个新的对象,不能作为锁对象
public/*synchronized//此处是为写在方法中的synchronized,和用synchronized块包含用this作为锁对象是一样的效果*/void p() {
synchronized (/* this */j) {
System.out.print("\n中国");
System.out.print("首都是");
}
System.out.print("北京\n");
}
public/*synchronized//此处是为写在方法中的synchronized,和用synchronized块包含用this作为锁对象是一样的效果*/void pp() {
synchronized (/* this */j) {
System.out.print("\n美国");
System.out.print("首都是");
}
System.out.print("纽约\n");
}
public static/*synchronized//此处因为是静态方法其默认将不是this锁,而是this类.class, 因为是静态的不属于某个对象所以默认是类的字节码作为锁的 */void ppp() {
synchronized (j/*this:因为是静态方法,代表当前对象类字节码锁对象,相当于pp或p方法中的synchronized(InnerClass<span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif;">.class){}*/) {</span>
System.out.print("\n英国");
System.out.print("首都是");
}
System.out.print("伦敦\n");
}
}
@Override
public void run() {
// InnerClass iClass = new
// InnerClass();//多线程在此处实例化则使用this作为锁对象将无法达到同步因为this的对象不一样
iClass.pp();
iClass.p();
iClass.ppp();
}
public static void main(String[] args) {
MutualClass mClass = new MutualClass();
new Thread(mClass, "线程A").start();
new Thread(mClass, "线程B").start();
new Thread(mClass, "线程C").start();
new Thread(mClass, "线程D").start();
new Thread(mClass, "线程E").start();
}
}
线程互斥同步总结:
因为多线程所以难免会产生不同步现象,会对数据的准确性照成破坏
所以我们需要同步数据块,使用线程的互斥
线程的互斥使用synchronized关键字,当其关键是对象锁,如果想要两个代码块互斥,需要使用同一个
对象锁,默认的方法上的对象锁是this,如果是静态方法则使用的是类.class作为默认对象锁
切记:如果想实现互斥,对象锁一定要一样
同步互斥代码例子:
public class MutualClass {
/**
* 子线程打印5次主线程打印10此如此循环15次
*
* @param args
*/
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 15; j++) {
business.sub(j);
}
}
}).start();
for (int j = 0; j < 15; j++) {
business.main(j);
}
}
}
class Business {
private boolean falg = true;
public synchronized void sub(int j) {
while (!falg) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int i = 0; i < 5; i++) {
System.out.println("子线程:打印" + i + "次,循环往复" + j + "次");
}
falg = false;
this.notify();
}
public synchronized void main(int j) {
while (falg) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int i = 0; i < 10; i++) {
System.out.println("主线程:打印" + i + "次,循环往复" + j + "次");
}
falg = true;
this.notify();
}
}
线程范围内共享变量
synchronized这类线程同步的机制可以解决多线程并发问题:这是一种以延长访问时间来换取线程安全性的策略。
而ThreadLocal类为每一个线程都维护了自己独有的变量拷贝:这是一种以空间来换取线程安全性的策略。
通过上面的代码我们可以使用同一对象中的变量的达到统一对象中多线程的变量共享,也就是上述demo3和demo4,但是有时候我们不需要共享变量而是每个线程对应操作对应的线程变量,即变量只限于同一个线程内部操作,此时我们就应该使用jdk的ThreadLocal类,存放在它里面的变量生命周期就是线程的周期,而且只限于同一线程操作线程内的变量,线程结束时候ThreadLocal中的变量也会被清除,其实现原理很简单,map<当前线程,变量>
demo:
import java.util.Random;
public class ThreadLClass implements Runnable {
ThreadLocal<Integer> tLocal = new ThreadLocal<>();
public ThreadLClass() {
}
@Override
public void run() {
int i = new Random().nextInt();
tLocal.set(i);
System.out.println(Thread.currentThread().getName() + ":存放数据" + i);
new A().get();
new B().get();
}
/**
* @param args
*/
public static void main(String[] args) {
ThreadLClass tClass = new ThreadLClass();
new Thread(tClass, "线程A").start();
new Thread(tClass, "线程B").start();
}
class A {
public void get() {
System.out.println(Thread.currentThread().getName() + ":获得数据"
+ tLocal.get());
}
}
class B {
public void get() {
System.out.println(Thread.currentThread().getName() + ":获得数据"
+ tLocal.get());
}
}
}